Skip to content

Commit 69c43a5

Browse files
committed
functor list page + functor detail page
1 parent ff0e08a commit 69c43a5

8 files changed

Lines changed: 337 additions & 0 deletions

File tree

src/components/FunctorList.svelte

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<script lang="ts">
2+
// TODO: remove code duplication with CategoryList component
3+
4+
import type { FunctorShort } from '$lib/commons/types'
5+
6+
type Props = {
7+
functors: FunctorShort[]
8+
description?: string
9+
}
10+
11+
let { description, functors }: Props = $props()
12+
</script>
13+
14+
{#if description}
15+
<p class="hint">
16+
{description}
17+
</p>
18+
{/if}
19+
20+
{#if functors.length}
21+
<ul>
22+
{#each functors as functor}
23+
<li>
24+
<a href="/functor/{functor.id}">
25+
{functor.name}
26+
</a>
27+
</li>
28+
{/each}
29+
</ul>
30+
{:else}
31+
<p>&mdash;</p>
32+
{/if}
33+
34+
<style>
35+
ul {
36+
margin-block: 1rem;
37+
}
38+
</style>

src/components/Nav.svelte

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@
5656
icon: faSearch,
5757
mode: 'categories',
5858
},
59+
{
60+
href: '/functors',
61+
text: 'Functors',
62+
nested: '/functor',
63+
icon: faDatabase,
64+
mode: 'functors',
65+
},
5966
{
6067
href: '/functor-properties',
6168
text: 'Properties',

src/components/NavMobile.svelte

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@
7171
</a>
7272
</li>
7373
{:else}
74+
<li>
75+
<a href="/functors">
76+
Functors <Fa icon={faDatabase} />
77+
</a>
78+
</li>
7479
<li>
7580
<a href="/functor-properties">
7681
Functor properties

src/lib/commons/types.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,23 @@ export type SpecialMorphism = {
8282
description: string
8383
reason: string
8484
}
85+
86+
export type FunctorShort = {
87+
id: string
88+
name: string
89+
}
90+
91+
export type FunctorDB = {
92+
id: string
93+
name: string
94+
source: string
95+
target: string
96+
source_name: string
97+
target_name: string
98+
description: string
99+
url: string | null
100+
}
101+
85102
export type FunctorPropertyDB = {
86103
id: string
87104
prefix: string
@@ -91,6 +108,8 @@ export type FunctorPropertyDB = {
91108
dual_property_id: string | null
92109
}
93110

111+
export type FunctorPropertyShort = Pick<FunctorPropertyDB, 'id' | 'prefix'>
112+
94113
export type FunctorProperty = Replace<
95114
FunctorPropertyDB,
96115
{
@@ -119,3 +138,15 @@ export type FunctorImplicationDisplay = Replace<
119138
target_assumptions: string[]
120139
}
121140
>
141+
142+
export type FunctorPropertyAssignmentDB = {
143+
id: string
144+
reason: string | null
145+
prefix: string
146+
is_deduced: number
147+
}
148+
149+
export type FunctorPropertyAssignment = Replace<
150+
FunctorPropertyAssignmentDB,
151+
{ is_deduced: boolean }
152+
>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import type {
2+
FunctorDB,
3+
FunctorPropertyAssignment,
4+
FunctorPropertyAssignmentDB,
5+
FunctorPropertyShort,
6+
} from '$lib/commons/types'
7+
import { batch } from '$lib/server/db'
8+
import { render_nested_formulas } from '$lib/server/rendering'
9+
import { error } from '@sveltejs/kit'
10+
import sql from 'sql-template-tag'
11+
12+
// remove code duplication with category detail page
13+
14+
export const load = async (event) => {
15+
const id = event.params.id
16+
17+
const { results, err } = await batch<
18+
[
19+
FunctorDB,
20+
FunctorPropertyAssignmentDB & { is_satisfied: number },
21+
FunctorPropertyShort,
22+
]
23+
>([
24+
sql`
25+
SELECT
26+
f.id, f.name, f.source, f.target, f.description, f.url,
27+
cs.name as source_name, ct.name as target_name
28+
FROM functors f
29+
INNER JOIN categories cs ON cs.id = f.source
30+
INNER JOIN categories ct ON ct.id = f.target
31+
WHERE f.id = ${id}
32+
`,
33+
// properties
34+
sql`
35+
SELECT
36+
fp.property_id AS id,
37+
fp.is_satisfied,
38+
fp.reason,
39+
fp.is_deduced,
40+
CASE
41+
WHEN fp.is_satisfied = TRUE THEN p.prefix
42+
ELSE pf.negation
43+
END AS prefix
44+
FROM functor_property_assignments fp
45+
INNER JOIN functor_properties p ON p.id = fp.property_id
46+
INNER JOIN prefixes pf ON pf.prefix = p.prefix
47+
WHERE fp.functor_id = ${id}
48+
ORDER BY fp.position, lower(fp.property_id)
49+
`,
50+
// unknown properties
51+
sql`
52+
SELECT
53+
p.id,
54+
p.prefix
55+
FROM functor_properties p
56+
WHERE NOT EXISTS (
57+
SELECT 1 FROM functor_property_assignments
58+
WHERE functor_id = ${id} AND property_id = p.id
59+
)
60+
ORDER BY lower(p.id)
61+
`,
62+
])
63+
64+
if (err) error(500, 'Could not load functor')
65+
66+
const [functors, properties_db, unknown_properties] = results
67+
68+
if (!functors.length) error(404, `Could not find functor with ID '${id}'`)
69+
70+
const [functor] = functors
71+
72+
const satisfied_properties: FunctorPropertyAssignment[] = properties_db
73+
.filter((obj) => obj.is_satisfied)
74+
.map((p) => ({
75+
id: p.id,
76+
reason: p.reason,
77+
is_deduced: Boolean(p.is_deduced),
78+
prefix: p.prefix,
79+
}))
80+
81+
const unsatisfied_properties: FunctorPropertyAssignment[] = properties_db
82+
.filter((obj) => !obj.is_satisfied)
83+
.map((p) => ({
84+
id: p.id,
85+
reason: p.reason,
86+
is_deduced: Boolean(p.is_deduced),
87+
prefix: p.prefix,
88+
}))
89+
90+
return render_nested_formulas({
91+
functor,
92+
satisfied_properties,
93+
unsatisfied_properties,
94+
unknown_properties,
95+
})
96+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<script lang="ts">
2+
import MetaData from '$components/MetaData.svelte'
3+
import PropertyList from '$components/PropertyList.svelte'
4+
import { category_detail_level } from '$lib/states/detail_level.svelte'
5+
let { data } = $props()
6+
7+
let functor = $derived(data.functor)
8+
</script>
9+
10+
<MetaData title={functor.name} description="Discover the properties of this functor" />
11+
12+
<h2>{functor.name}</h2>
13+
14+
<section aria-label="main info" class="main">
15+
<ul class="with-margins">
16+
<li>
17+
<strong>Source:</strong>
18+
<a href="/category/{functor.source}">{functor.source_name}</a>
19+
</li>
20+
21+
<li>
22+
<strong>Target:</strong>
23+
<a href="/category/{functor.target}">{functor.target_name}</a>
24+
</li>
25+
26+
{#if functor.url}
27+
<li>
28+
<strong>URL:</strong>
29+
<a href={functor.url}>Link</a>
30+
</li>
31+
{/if}
32+
</ul>
33+
34+
<p>{@html functor.description}</p>
35+
</section>
36+
37+
<!-- TODO: remove code duplication with category detail page -->
38+
{#key functor.id}
39+
<div class="two-columns">
40+
<section>
41+
<h3>Satisfied Properties</h3>
42+
43+
{#if category_detail_level.value === 'all'}
44+
<PropertyList
45+
properties={data.satisfied_properties.filter((p) => !p.is_deduced)}
46+
description="Properties from the database"
47+
type="functor"
48+
/>
49+
50+
<PropertyList
51+
properties={data.satisfied_properties.filter((p) => p.is_deduced)}
52+
description="Deduced properties"
53+
type="functor"
54+
/>
55+
{:else if category_detail_level.value === 'merged'}
56+
<PropertyList properties={data.satisfied_properties} type="functor" />
57+
{:else if category_detail_level.value === 'basic'}
58+
<PropertyList
59+
properties={data.satisfied_properties.filter((p) => !p.is_deduced)}
60+
description="Properties from the database; further properties can be deduced."
61+
type="functor"
62+
/>
63+
{/if}
64+
</section>
65+
66+
<section>
67+
<h3>Unsatisfied Properties</h3>
68+
69+
{#if category_detail_level.value === 'all'}
70+
<PropertyList
71+
properties={data.unsatisfied_properties.filter((p) => !p.is_deduced)}
72+
description="Properties from the database"
73+
type="functor"
74+
/>
75+
76+
<PropertyList
77+
properties={data.unsatisfied_properties.filter((p) => p.is_deduced)}
78+
description="Deduced properties*"
79+
type="functor"
80+
/>
81+
82+
<p class="hint">*This also uses the deduced satisfied properties.</p>
83+
{:else if category_detail_level.value === 'merged'}
84+
<PropertyList properties={data.unsatisfied_properties} type="functor" />
85+
{:else if category_detail_level.value === 'basic'}
86+
<PropertyList
87+
properties={data.unsatisfied_properties.filter((p) => !p.is_deduced)}
88+
description="Properties from the database; further properties can be deduced."
89+
type="functor"
90+
/>
91+
{/if}
92+
</section>
93+
</div>
94+
95+
<section>
96+
<h3>Unknown properties</h3>
97+
98+
<PropertyList
99+
properties={data.unknown_properties}
100+
description={data.unknown_properties.length
101+
? "For these properties the database currently doesn't have an answer if they are satisfied or not. Please help to complete the data!"
102+
: undefined}
103+
/>
104+
</section>
105+
{/key}
106+
107+
<style>
108+
.main {
109+
margin-top: 1.5rem;
110+
}
111+
112+
@media (min-width: 720px) {
113+
.two-columns {
114+
display: grid;
115+
grid-template-columns: 1fr 1fr;
116+
gap: 0.5rem;
117+
}
118+
}
119+
120+
ul.with-margins li + li {
121+
margin-top: 0.25rem;
122+
}
123+
</style>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { FunctorShort } from '$lib/commons/types'
2+
import { query } from '$lib/server/db'
3+
import { error } from '@sveltejs/kit'
4+
import sql from 'sql-template-tag'
5+
6+
export const prerender = true
7+
8+
export const load = async () => {
9+
const { rows: functors, err } = await query<FunctorShort>(
10+
sql`SELECT id, name FROM functors ORDER BY lower(name)`,
11+
)
12+
13+
if (err) error(500, 'Functors could not be loaded')
14+
15+
return { functors }
16+
}

src/routes/functors/+page.svelte

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script lang="ts">
2+
import FunctorList from '$components/FunctorList.svelte'
3+
import MetaData from '$components/MetaData.svelte'
4+
import { pluralize } from '$lib/client/utils'
5+
6+
let { data } = $props()
7+
</script>
8+
9+
<MetaData title="List of functors" description="All available functors" />
10+
11+
<section>
12+
<h2>List of functors</h2>
13+
14+
<FunctorList
15+
functors={data.functors}
16+
description={pluralize(data.functors.length, {
17+
one: 'Found {count} functor',
18+
other: 'Found {count} functors',
19+
})}
20+
/>
21+
</section>

0 commit comments

Comments
 (0)