Skip to content

Commit

Permalink
Added Movie Database DBpedia example (#103)
Browse files Browse the repository at this point in the history
Resolves #92.
  • Loading branch information
karelklima authored Jan 15, 2024
1 parent 76830a1 commit 9a52cc3
Show file tree
Hide file tree
Showing 30 changed files with 955 additions and 3 deletions.
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,3 @@ node_modules

package-lock.json
ontologies

next-movie-database
react-movie-database
3 changes: 3 additions & 0 deletions examples/next-movie-database/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["next", "next/core-web-vitals", "prettier"]
}
36 changes: 36 additions & 0 deletions examples/next-movie-database/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# local env files
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
5 changes: 5 additions & 0 deletions examples/next-movie-database/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2
}
20 changes: 20 additions & 0 deletions examples/next-movie-database/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Linked Data Movie Database

This is a IMDb-like movie database based on Linked Data and DBpedia.

Build with [LDkit](https://ldkit.io), React, TailwindCSS and Next.js.

## Getting Started

1. Install dependencies
```bash
npm install
```

2. Run the development server:

```bash
npm run dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
Binary file added examples/next-movie-database/app/favicon.ico
Binary file not shown.
21 changes: 21 additions & 0 deletions examples/next-movie-database/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 255, 255, 255;
--background-end-rgb: 255, 255, 255;
}

@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}

body {
color: rgb(var(--foreground-rgb));
}
27 changes: 27 additions & 0 deletions examples/next-movie-database/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Metadata } from "next";
import "./globals.css";
import { Header } from "@/components/Header";
import { Footer } from "@/components/Footer";

export const metadata: Metadata = {
title: "Linked Data Movie Database",
description: "Browse and search movies and actors from Wikidata",
};

export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" className="bg-black min-h-screen">
<body className="bg-white">
<Header />
<main className="container max-w-6xl mx-auto px-8 py-8 min-h-[80vh]">
{children}
</main>
<Footer />
</body>
</html>
);
}
108 changes: 108 additions & 0 deletions examples/next-movie-database/app/movie/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { Suspense } from "react";

import {
Movies,
MoviesActors,
MoviesDirectors,
MoviesWriters,
MoviesComposers,
} from "@/data/lens";
import { Thumbnail } from "@/components/Thumbnail";
import { Links } from "@/components/Links";
import { Loading } from "@/components/Loading";

export default async function MoviePage({
searchParams,
}: {
searchParams: Record<string, string>;
}) {
const { iri } = searchParams;

if (!iri || iri.length < 1) {
return <h1>No IRI found!</h1>;
}

const movie = await Movies.findOne({
$id: iri,
name: {
$langMatches: "en",
},
abstract: {
$langMatches: "en",
},
});

if (movie == null) {
return <h1>No movie found!</h1>;
}

return (
<div className="grid grid-cols-4 gap-4">
<article className="prose max-w-full pr-8 col-span-3">
<h1>{movie.name}</h1>
<p>{movie.abstract}</p>
<h2>Director</h2>

<Suspense fallback={<Loading />}>
<Director iri={movie.$id} />
</Suspense>
<h2>Cast</h2>
<Suspense fallback={<Loading />}>
<Cast iri={movie.$id} />
</Suspense>
<h2>Writer</h2>
<Suspense fallback={<Loading />}>
<Writer iri={movie.$id} />
</Suspense>
<h2>Composer</h2>
<Suspense fallback={<Loading />}>
<Composer iri={movie.$id} />
</Suspense>
</article>
<aside className="col-span-1 prose">
<Thumbnail imageUrl={movie.thumbnail} />
<Links iri={movie.$id} />
</aside>
</div>
);
}

async function Director({ iri }: { iri: string }) {
const movieWithDirectors = await MoviesDirectors.findByIri(iri);
return <PersonList>{movieWithDirectors?.directors}</PersonList>;
}

async function Writer({ iri }: { iri: string }) {
const movieWithWriters = await MoviesWriters.findByIri(iri);
return <PersonList>{movieWithWriters?.writers}</PersonList>;
}

async function Composer({ iri }: { iri: string }) {
const movieWithComposers = await MoviesComposers.findByIri(iri);
return <PersonList>{movieWithComposers?.composers}</PersonList>;
}

async function Cast({ iri }: { iri: string }) {
const movieWithActors = await MoviesActors.findByIri(iri);
return <PersonList>{movieWithActors?.starring}</PersonList>;
}

function PersonList({
children,
}: {
children: { $id: string; name: string }[] | undefined;
}) {
if (children === undefined || children.length < 1) {
return <p>No records found.</p>;
}

return (
<ul>
{children.map((person) => (
<li key={person.$id}>
<a href={`/person?iri=${person.$id}`}>{person.name}</a>
</li>
))}
</ul>
);
}
127 changes: 127 additions & 0 deletions examples/next-movie-database/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { Loading } from "@/components/Loading";
import { Search, SearchInterface } from "@/data/lens";
import { yago } from "@/data/namespaces";
import { schema } from "ldkit/namespaces";
import { Suspense } from "react";

export default function Home() {
return (
<>
<p className="text-center text-lg pb-8 pt-2">
Use the search bar above to look for people or movies, or pick some
selection below.
</p>
<div className="grid grid-cols-3">
<section className="prose">
<h1>Top Movies</h1>
<Suspense fallback={<Loading />}>
<TopMovies />
</Suspense>
</section>
<section className="prose">
<h1>Top Actors</h1>
<Suspense fallback={<Loading />}>
<TopActors />
</Suspense>
</section>
<section className="prose">
<h1>Top Directors</h1>
<Suspense fallback={<Loading />}>
<TopDirectors />
</Suspense>
</section>
</div>
</>
);
}

async function TopMovies() {
const movies = await search(
[
"http://dbpedia.org/resource/The_Shawshank_Redemption",
"http://dbpedia.org/resource/The_Godfather",
"http://dbpedia.org/resource/The_Dark_Knight",
"http://dbpedia.org/resource/Pulp_Fiction",
"http://dbpedia.org/resource/Schindler's_List",
"http://dbpedia.org/resource/The_Matrix",
"http://dbpedia.org/resource/Fight_Club",
"http://dbpedia.org/resource/Goodfellas",
"http://dbpedia.org/resource/Terminator_2:_Judgment_Day",
"http://dbpedia.org/resource/Inception",
],
schema.Movie
);
return <SearchResults items={movies} />;
}

async function TopActors() {
const movies = await search(
[
"http://dbpedia.org/resource/Christian_Bale",
"http://dbpedia.org/resource/Helena_Bonham_Carter",
"http://dbpedia.org/resource/Robert_De_Niro",
"http://dbpedia.org/resource/Al_Pacino",
"http://dbpedia.org/resource/Tom_Hanks",
"http://dbpedia.org/resource/Leonardo_DiCaprio",
"http://dbpedia.org/resource/Meryl_Streep",
"http://dbpedia.org/resource/Brad_Pitt",
"http://dbpedia.org/resource/Tom_Cruise",
"http://dbpedia.org/resource/Sigourney_Weaver",
],
yago.Actor109765278
);
return <SearchResults items={movies} />;
}

async function TopDirectors() {
const movies = await search(
[
"http://dbpedia.org/resource/Stanley_Kubrick",
"http://dbpedia.org/resource/Quentin_Tarantino",
"http://dbpedia.org/resource/Christopher_Nolan",
"http://dbpedia.org/resource/Ridley_Scott",
"http://dbpedia.org/resource/Steven_Spielberg",
"http://dbpedia.org/resource/Martin_Scorsese",
"http://dbpedia.org/resource/Alfred_Hitchcock",
"http://dbpedia.org/resource/David_Fincher",
"http://dbpedia.org/resource/Tim_Burton",
"http://dbpedia.org/resource/Guillermo_del_Toro",
],
yago.Director110014939
);
return <SearchResults items={movies} />;
}

function SearchResults({ items }: { items: SearchInterface[] }) {
return (
<ul>
{items.map((x) => (
<SearchResult key={x.$id} {...x} />
))}
</ul>
);
}

function SearchResult(props: SearchInterface) {
const mode = props.types.includes(schema.Movie) ? "movie" : "person";

return (
<li>
<a href={`/${mode}?iri=${props.$id}`}>{props.name}</a>
</li>
);
}

async function search(ids: string[], type: string) {
return Search.find({
where: {
$id: ids,
name: {
$langMatches: "en",
},
types: {
$in: [type],
},
},
});
}
Loading

0 comments on commit 9a52cc3

Please sign in to comment.