Commit
·
3d330e9
1
Parent(s):
36f8d04
✨ Admin page for pages
Browse files- src/lib/server/db/index.ts +1 -1
- src/lib/server/db/page.ts +38 -10
- src/lib/types/Page.ts +11 -41
- src/lib/utils/typedKeys.ts +3 -0
- src/routes/+layout.svelte +3 -3
- src/routes/+page.svelte +2 -1
- src/routes/admin/+layout.server.ts +12 -0
- src/routes/admin/+layout.svelte +26 -0
- src/routes/admin/+page.ts +6 -0
- src/routes/admin/pages/+page.server.ts +10 -0
- src/routes/admin/pages/+page.svelte +61 -0
- src/routes/admin/pages/[id]/+server.ts +30 -0
- src/routes/realisations/+page.svelte +2 -1
src/lib/server/db/index.ts
CHANGED
@@ -19,4 +19,4 @@ const products = createProductCollection(db);
|
|
19 |
const { pictures, picturesFs } = createPictureCollections(db);
|
20 |
|
21 |
export { client, db, pages, users, pictures, picturesFs, products };
|
22 |
-
export const collections = { products, pictures };
|
|
|
19 |
const { pictures, picturesFs } = createPictureCollections(db);
|
20 |
|
21 |
export { client, db, pages, users, pictures, picturesFs, products };
|
22 |
+
export const collections = { products, pictures, pages, users };
|
src/lib/server/db/page.ts
CHANGED
@@ -28,7 +28,7 @@ Des tissus rigoureusement sélectionnés vous seront proposés pour habiller vos
|
|
28 |
|
29 |
Daphné ne travaille que sur rendez vous, alors n'hésitez pas à la contacter, par téléphone ou par mail pour toute demande.`,
|
30 |
'eshop-description': "description de l'eshop",
|
31 |
-
description: `C'est dans son univers enchanteur que Daphné le Couls, tapissière d'ameublement qualifiée depuis 2019, vous propose la réfection de vos assises dans son atelier situé en Finistère, à logonna Daoulas (entre l'axe Brest Quimper).
|
32 |
|
33 |
Daphné se déplace à votre domicile afin de déterminer avec vous vos besoins, qu'il s'agisse d'une réfection de siège complète, ou bien de la création de coussins décoratifs.`
|
34 |
},
|
@@ -46,17 +46,23 @@ Daphné se déplace à votre domicile afin de déterminer avec vous vos besoins,
|
|
46 |
'realisation-8': null,
|
47 |
'realisation-9': null,
|
48 |
'realisation-10': null
|
49 |
-
}
|
|
|
|
|
50 |
} as HomePage,
|
51 |
'/contact': {
|
52 |
_id: '/contact',
|
53 |
name: 'Contact',
|
54 |
text: {
|
|
|
|
|
55 |
description: 'Je me déplace à votre domicile dans le Finistère sur rendez-vous.'
|
56 |
},
|
57 |
pictures: {
|
58 |
'photo-garde': null
|
59 |
-
}
|
|
|
|
|
60 |
} as ContactPage,
|
61 |
'/atelier': {
|
62 |
_id: '/atelier',
|
@@ -70,7 +76,7 @@ Daphné se déplace à votre domicile afin de déterminer avec vous vos besoins,
|
|
70 |
'texte-2': `Nous vous proposons un service en ligne afin de concevoir à distance des coussins, et de vous les livrer n'importe où en France.
|
71 |
|
72 |
Nous proposons à la vente également des assises déjà refectionnées dans la partie E-shop.`,
|
73 |
-
description:
|
74 |
"À l'atelier, nous vous proposons la réfection traditionnelle ou moderne de vos assises (crin ou mousse) selon vos besoins."
|
75 |
},
|
76 |
pictures: {
|
@@ -83,7 +89,7 @@ Nous proposons à la vente également des assises déjà refectionnées dans la
|
|
83 |
_id: '/realisations',
|
84 |
name: 'Réalisations',
|
85 |
text: {
|
86 |
-
description:
|
87 |
"Découvrez les sièges, fauteuils et coussins réalisés par Daphné, tapissière d'ameublement de la Bergère Enchantée",
|
88 |
'realisation-1': '',
|
89 |
'realisation-2': '',
|
@@ -94,7 +100,17 @@ Nous proposons à la vente également des assises déjà refectionnées dans la
|
|
94 |
'realisation-7': '',
|
95 |
'realisation-8': '',
|
96 |
'realisation-9': '',
|
97 |
-
'realisation-10': ''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
98 |
},
|
99 |
pictures: {
|
100 |
'realisation-1': null,
|
@@ -106,8 +122,20 @@ Nous proposons à la vente également des assises déjà refectionnées dans la
|
|
106 |
'realisation-7': null,
|
107 |
'realisation-8': null,
|
108 |
'realisation-9': null,
|
109 |
-
'realisation-10': null
|
110 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
} as CreationsPage,
|
112 |
'/tissus-et-finitions': {
|
113 |
_id: '/tissus-et-finitions',
|
@@ -130,7 +158,7 @@ Nous proposons à la vente également des assises déjà refectionnées dans la
|
|
130 |
'photo-15': null
|
131 |
},
|
132 |
text: {
|
133 |
-
description:
|
134 |
"Découvrez les finitions et tissus utilisés par Daphné, tapissière d'ameublement de la Bergère Enchantée"
|
135 |
}
|
136 |
} as FabricsPage,
|
@@ -138,7 +166,7 @@ Nous proposons à la vente également des assises déjà refectionnées dans la
|
|
138 |
_id: '/vente',
|
139 |
name: 'E-shop',
|
140 |
text: {
|
141 |
-
description:
|
142 |
'Liste des fauteuils, chaises, coussins... réalisés par Daphné et disponibles à la vente'
|
143 |
},
|
144 |
pictures: {
|
|
|
28 |
|
29 |
Daphné ne travaille que sur rendez vous, alors n'hésitez pas à la contacter, par téléphone ou par mail pour toute demande.`,
|
30 |
'eshop-description': "description de l'eshop",
|
31 |
+
'search-engine-description': `C'est dans son univers enchanteur que Daphné le Couls, tapissière d'ameublement qualifiée depuis 2019, vous propose la réfection de vos assises dans son atelier situé en Finistère, à logonna Daoulas (entre l'axe Brest Quimper).
|
32 |
|
33 |
Daphné se déplace à votre domicile afin de déterminer avec vous vos besoins, qu'il s'agisse d'une réfection de siège complète, ou bien de la création de coussins décoratifs.`
|
34 |
},
|
|
|
46 |
'realisation-8': null,
|
47 |
'realisation-9': null,
|
48 |
'realisation-10': null
|
49 |
+
},
|
50 |
+
createdAt: new Date(),
|
51 |
+
updatedAt: new Date()
|
52 |
} as HomePage,
|
53 |
'/contact': {
|
54 |
_id: '/contact',
|
55 |
name: 'Contact',
|
56 |
text: {
|
57 |
+
'search-engine-description':
|
58 |
+
'Je me déplace à votre domicile dans le Finistère sur rendez-vous.',
|
59 |
description: 'Je me déplace à votre domicile dans le Finistère sur rendez-vous.'
|
60 |
},
|
61 |
pictures: {
|
62 |
'photo-garde': null
|
63 |
+
},
|
64 |
+
createdAt: new Date(),
|
65 |
+
updatedAt: new Date()
|
66 |
} as ContactPage,
|
67 |
'/atelier': {
|
68 |
_id: '/atelier',
|
|
|
76 |
'texte-2': `Nous vous proposons un service en ligne afin de concevoir à distance des coussins, et de vous les livrer n'importe où en France.
|
77 |
|
78 |
Nous proposons à la vente également des assises déjà refectionnées dans la partie E-shop.`,
|
79 |
+
'search-engine-description':
|
80 |
"À l'atelier, nous vous proposons la réfection traditionnelle ou moderne de vos assises (crin ou mousse) selon vos besoins."
|
81 |
},
|
82 |
pictures: {
|
|
|
89 |
_id: '/realisations',
|
90 |
name: 'Réalisations',
|
91 |
text: {
|
92 |
+
'search-engine-description':
|
93 |
"Découvrez les sièges, fauteuils et coussins réalisés par Daphné, tapissière d'ameublement de la Bergère Enchantée",
|
94 |
'realisation-1': '',
|
95 |
'realisation-2': '',
|
|
|
100 |
'realisation-7': '',
|
101 |
'realisation-8': '',
|
102 |
'realisation-9': '',
|
103 |
+
'realisation-10': '',
|
104 |
+
'realisation-11': '',
|
105 |
+
'realisation-12': '',
|
106 |
+
'realisation-13': '',
|
107 |
+
'realisation-14': '',
|
108 |
+
'realisation-15': '',
|
109 |
+
'realisation-16': '',
|
110 |
+
'realisation-17': '',
|
111 |
+
'realisation-18': '',
|
112 |
+
'realisation-19': '',
|
113 |
+
'realisation-20': ''
|
114 |
},
|
115 |
pictures: {
|
116 |
'realisation-1': null,
|
|
|
122 |
'realisation-7': null,
|
123 |
'realisation-8': null,
|
124 |
'realisation-9': null,
|
125 |
+
'realisation-10': null,
|
126 |
+
'realisation-11': null,
|
127 |
+
'realisation-12': null,
|
128 |
+
'realisation-13': null,
|
129 |
+
'realisation-14': null,
|
130 |
+
'realisation-15': null,
|
131 |
+
'realisation-16': null,
|
132 |
+
'realisation-17': null,
|
133 |
+
'realisation-18': null,
|
134 |
+
'realisation-19': null,
|
135 |
+
'realisation-20': null
|
136 |
+
},
|
137 |
+
createdAt: new Date(),
|
138 |
+
updatedAt: new Date()
|
139 |
} as CreationsPage,
|
140 |
'/tissus-et-finitions': {
|
141 |
_id: '/tissus-et-finitions',
|
|
|
158 |
'photo-15': null
|
159 |
},
|
160 |
text: {
|
161 |
+
'search-engine-description':
|
162 |
"Découvrez les finitions et tissus utilisés par Daphné, tapissière d'ameublement de la Bergère Enchantée"
|
163 |
}
|
164 |
} as FabricsPage,
|
|
|
166 |
_id: '/vente',
|
167 |
name: 'E-shop',
|
168 |
text: {
|
169 |
+
'search-engine-description':
|
170 |
'Liste des fauteuils, chaises, coussins... réalisés par Daphné et disponibles à la vente'
|
171 |
},
|
172 |
pictures: {
|
src/lib/types/Page.ts
CHANGED
@@ -3,8 +3,8 @@ import type { Timestamps } from './Timestamps';
|
|
3 |
export interface Page extends Timestamps {
|
4 |
_id: string;
|
5 |
name: string;
|
6 |
-
text: Record<string, string>;
|
7 |
-
pictures: Record<string, string | null>;
|
8 |
}
|
9 |
|
10 |
export interface HomePage extends Page {
|
@@ -13,60 +13,29 @@ export interface HomePage extends Page {
|
|
13 |
text: {
|
14 |
presentation: string;
|
15 |
'eshop-description': string;
|
16 |
-
description: string;
|
17 |
};
|
18 |
pictures: {
|
19 |
discover: string | null;
|
20 |
move: string | null;
|
21 |
'e-shop': string | null;
|
22 |
-
|
23 |
-
'realisation-2': string | null;
|
24 |
-
'realisation-3': string | null;
|
25 |
-
'realisation-4': string | null;
|
26 |
-
'realisation-5': string | null;
|
27 |
-
'realisation-6': string | null;
|
28 |
-
'realisation-7': string | null;
|
29 |
-
'realisation-8': string | null;
|
30 |
-
'realisation-9': string | null;
|
31 |
-
'realisation-10': string | null;
|
32 |
-
};
|
33 |
}
|
34 |
|
35 |
export interface CreationsPage extends Page {
|
36 |
_id: '/realisations';
|
37 |
name: 'Réalisations';
|
38 |
text: {
|
39 |
-
description: string;
|
40 |
-
|
41 |
-
|
42 |
-
'realisation-3': string;
|
43 |
-
'realisation-4': string;
|
44 |
-
'realisation-5': string;
|
45 |
-
'realisation-6': string;
|
46 |
-
'realisation-7': string;
|
47 |
-
'realisation-8': string;
|
48 |
-
'realisation-9': string;
|
49 |
-
'realisation-10': string;
|
50 |
-
};
|
51 |
-
pictures: {
|
52 |
-
'realisation-1': string | null;
|
53 |
-
'realisation-2': string | null;
|
54 |
-
'realisation-3': string | null;
|
55 |
-
'realisation-4': string | null;
|
56 |
-
'realisation-5': string | null;
|
57 |
-
'realisation-6': string | null;
|
58 |
-
'realisation-7': string | null;
|
59 |
-
'realisation-8': string | null;
|
60 |
-
'realisation-9': string | null;
|
61 |
-
'realisation-10': string | null;
|
62 |
-
};
|
63 |
}
|
64 |
|
65 |
export interface FabricsPage {
|
66 |
_id: '/tissus-et-finitions';
|
67 |
name: 'Tissus et finitions';
|
68 |
text: {
|
69 |
-
description: string;
|
70 |
};
|
71 |
pictures: {
|
72 |
'photo-1': string | null;
|
@@ -92,6 +61,7 @@ export interface ContactPage extends Page {
|
|
92 |
name: 'Contact';
|
93 |
text: {
|
94 |
description: string;
|
|
|
95 |
};
|
96 |
pictures: {
|
97 |
'photo-garde': string | null;
|
@@ -102,7 +72,7 @@ export interface WorkshopPage extends Page {
|
|
102 |
_id: '/atelier';
|
103 |
name: "L'Atelier";
|
104 |
text: {
|
105 |
-
description: string;
|
106 |
'texte-1': string;
|
107 |
'texte-2': string;
|
108 |
};
|
@@ -117,7 +87,7 @@ export interface EshopPage extends Page {
|
|
117 |
_id: '/vente';
|
118 |
name: 'E-shop';
|
119 |
text: {
|
120 |
-
description: string;
|
121 |
};
|
122 |
pictures: {
|
123 |
background: string | null;
|
|
|
3 |
export interface Page extends Timestamps {
|
4 |
_id: string;
|
5 |
name: string;
|
6 |
+
text: Record<string, string | undefined>;
|
7 |
+
pictures: Record<string, string | null | undefined>;
|
8 |
}
|
9 |
|
10 |
export interface HomePage extends Page {
|
|
|
13 |
text: {
|
14 |
presentation: string;
|
15 |
'eshop-description': string;
|
16 |
+
'search-engine-description': string;
|
17 |
};
|
18 |
pictures: {
|
19 |
discover: string | null;
|
20 |
move: string | null;
|
21 |
'e-shop': string | null;
|
22 |
+
} & Partial<Record<`realisation-${number}`, string | null>>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
}
|
24 |
|
25 |
export interface CreationsPage extends Page {
|
26 |
_id: '/realisations';
|
27 |
name: 'Réalisations';
|
28 |
text: {
|
29 |
+
'search-engine-description': string;
|
30 |
+
} & Partial<Record<`realisation-${number}`, string>>;
|
31 |
+
pictures: Partial<Record<`realisation-${number}`, string | null>>;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
}
|
33 |
|
34 |
export interface FabricsPage {
|
35 |
_id: '/tissus-et-finitions';
|
36 |
name: 'Tissus et finitions';
|
37 |
text: {
|
38 |
+
'search-engine-description': string;
|
39 |
};
|
40 |
pictures: {
|
41 |
'photo-1': string | null;
|
|
|
61 |
name: 'Contact';
|
62 |
text: {
|
63 |
description: string;
|
64 |
+
'search-engine-description': string;
|
65 |
};
|
66 |
pictures: {
|
67 |
'photo-garde': string | null;
|
|
|
72 |
_id: '/atelier';
|
73 |
name: "L'Atelier";
|
74 |
text: {
|
75 |
+
'search-engine-description': string;
|
76 |
'texte-1': string;
|
77 |
'texte-2': string;
|
78 |
};
|
|
|
87 |
_id: '/vente';
|
88 |
name: 'E-shop';
|
89 |
text: {
|
90 |
+
'search-engine-description': string;
|
91 |
};
|
92 |
pictures: {
|
93 |
background: string | null;
|
src/lib/utils/typedKeys.ts
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
export function typedKeys<K extends string>(x: Record<K, any>): K[] {
|
2 |
+
return Object.keys(x) as K[];
|
3 |
+
}
|
src/routes/+layout.svelte
CHANGED
@@ -24,9 +24,9 @@
|
|
24 |
<meta property="twitter:title" content={$page.data.title} />
|
25 |
{/if}
|
26 |
{#if $page.data.description}
|
27 |
-
<meta name="description" content={$page.data
|
28 |
-
<meta property="og:description" content={$page.data
|
29 |
-
<meta property="twitter:description" content={$page.data
|
30 |
{/if}
|
31 |
<meta property="og:type" content={$page.data.type || 'website'} />
|
32 |
{#if shownPicture}
|
|
|
24 |
<meta property="twitter:title" content={$page.data.title} />
|
25 |
{/if}
|
26 |
{#if $page.data.description}
|
27 |
+
<meta name="description" content={$page.data['search-engine-description']} />
|
28 |
+
<meta property="og:description" content={$page.data['search-engine-description']} />
|
29 |
+
<meta property="twitter:description" content={$page.data['search-engine-description']} />
|
30 |
{/if}
|
31 |
<meta property="og:type" content={$page.data.type || 'website'} />
|
32 |
{#if shownPicture}
|
src/routes/+page.svelte
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
import Container from '$lib/components/Container.svelte';
|
4 |
import Picture from '$lib/components/Picture.svelte';
|
5 |
import type { HomePage } from '$lib/types/Page';
|
|
|
6 |
import { marked } from 'marked';
|
7 |
import type { PageData } from './$types';
|
8 |
|
@@ -13,7 +14,7 @@
|
|
13 |
|
14 |
type PictureKey = keyof typeof pageData.pictures;
|
15 |
|
16 |
-
const showcasePics =
|
17 |
.filter((key) => key.startsWith('realisation-') && pageData.pictures[key as PictureKey])
|
18 |
.map((key) => pictures.find((pic) => pic._id === pageData.pictures[key as PictureKey]))
|
19 |
.filter(Boolean);
|
|
|
3 |
import Container from '$lib/components/Container.svelte';
|
4 |
import Picture from '$lib/components/Picture.svelte';
|
5 |
import type { HomePage } from '$lib/types/Page';
|
6 |
+
import { typedKeys } from '$lib/utils/typedKeys';
|
7 |
import { marked } from 'marked';
|
8 |
import type { PageData } from './$types';
|
9 |
|
|
|
14 |
|
15 |
type PictureKey = keyof typeof pageData.pictures;
|
16 |
|
17 |
+
const showcasePics = typedKeys(pageData.pictures)
|
18 |
.filter((key) => key.startsWith('realisation-') && pageData.pictures[key as PictureKey])
|
19 |
.map((key) => pictures.find((pic) => pic._id === pageData.pictures[key as PictureKey]))
|
20 |
.filter(Boolean);
|
src/routes/admin/+layout.server.ts
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import type { LayoutServerLoad } from './$types';
|
2 |
+
import { error, redirect } from '@sveltejs/kit';
|
3 |
+
|
4 |
+
export const load: LayoutServerLoad = (event) => {
|
5 |
+
if (!event.locals.user) {
|
6 |
+
throw redirect(303, `/connexion?suivant=${encodeURIComponent(event.url.href)}`);
|
7 |
+
}
|
8 |
+
|
9 |
+
if (event.locals.user.authority !== 'admin') {
|
10 |
+
throw error(403);
|
11 |
+
}
|
12 |
+
};
|
src/routes/admin/+layout.svelte
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { page } from '$app/stores';
|
3 |
+
import Container from '$lib/components/Container.svelte';
|
4 |
+
</script>
|
5 |
+
|
6 |
+
<div class="flex justify-evenly mb-10">
|
7 |
+
<a
|
8 |
+
href="/admin/pages"
|
9 |
+
class="pa-2 link"
|
10 |
+
class:text-sunray={$page.url.pathname.startsWith('/admin/pages')}>Pages</a
|
11 |
+
>
|
12 |
+
<a
|
13 |
+
href="/admin/photos"
|
14 |
+
class="pa-2 link"
|
15 |
+
class:text-sunray={$page.url.pathname.startsWith('/admin/photos')}>Photos</a
|
16 |
+
>
|
17 |
+
<a
|
18 |
+
href="/admin/produits"
|
19 |
+
class="pa-2 link"
|
20 |
+
class:text-sunray={$page.url.pathname.startsWith('/admin/produits')}>Produits</a
|
21 |
+
>
|
22 |
+
</div>
|
23 |
+
|
24 |
+
<Container>
|
25 |
+
<slot />
|
26 |
+
</Container>
|
src/routes/admin/+page.ts
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import type { PageLoad } from './$types';
|
2 |
+
import { redirect } from '@sveltejs/kit';
|
3 |
+
|
4 |
+
export const load: PageLoad = () => {
|
5 |
+
throw redirect(302, '/admin/pages');
|
6 |
+
};
|
src/routes/admin/pages/+page.server.ts
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { pictures } from '$lib/server/db';
|
2 |
+
import { pages } from '$lib/server/db/page';
|
3 |
+
import type { PageServerLoad } from './$types';
|
4 |
+
|
5 |
+
export const load: PageServerLoad = async () => {
|
6 |
+
return {
|
7 |
+
pages: Object.values(pages),
|
8 |
+
photos: await pictures.find({ productId: { $exists: false } }).toArray()
|
9 |
+
};
|
10 |
+
};
|
src/routes/admin/pages/+page.svelte
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import type { Page } from '$lib/types/Page';
|
3 |
+
import type { PageData } from './$types';
|
4 |
+
|
5 |
+
export let data: PageData;
|
6 |
+
|
7 |
+
function updatePicture(page: Page, key: string, value: string) {
|
8 |
+
fetch('/admin/pages/' + encodeURIComponent(page._id), {
|
9 |
+
method: 'POST',
|
10 |
+
headers: { 'content-type': 'application/json' },
|
11 |
+
body: JSON.stringify({ type: 'picture', key, value })
|
12 |
+
});
|
13 |
+
}
|
14 |
+
|
15 |
+
function updateText(page: Page, key: string, value: string) {
|
16 |
+
fetch('/admin/pages/' + encodeURIComponent(page._id), {
|
17 |
+
method: 'POST',
|
18 |
+
headers: { 'content-type': 'application/json' },
|
19 |
+
body: JSON.stringify({ type: 'text', key, value })
|
20 |
+
});
|
21 |
+
}
|
22 |
+
</script>
|
23 |
+
|
24 |
+
{#each data.pages as page}
|
25 |
+
<section class="mb-6">
|
26 |
+
<h1>{page.name} ({page._id})</h1>
|
27 |
+
|
28 |
+
<h2 class="mt-4">Textes</h2>
|
29 |
+
|
30 |
+
{#each Object.keys(page.text) as key}
|
31 |
+
<label class="block w-full mt-4">
|
32 |
+
<h3>{key}</h3>
|
33 |
+
<textarea
|
34 |
+
name="{page._id}_text_{key}"
|
35 |
+
cols="30"
|
36 |
+
rows="10"
|
37 |
+
class="block w-full"
|
38 |
+
value={page.text[key]}
|
39 |
+
on:blur={(event) => updateText(page, key, event.currentTarget.value)}
|
40 |
+
/>
|
41 |
+
</label>
|
42 |
+
{/each}
|
43 |
+
|
44 |
+
<h2 class="mt-4">Images</h2>
|
45 |
+
|
46 |
+
{#each Object.keys(page.pictures) as key}
|
47 |
+
<label class="block w-full mt-4">
|
48 |
+
<h3>{key}</h3>
|
49 |
+
<select
|
50 |
+
name="{page._id}_picture_{key}"
|
51 |
+
value={page.pictures[key]}
|
52 |
+
on:change={(event) => updatePicture(page, key, event.currentTarget.value)}
|
53 |
+
>
|
54 |
+
{#each data.photos as photo}
|
55 |
+
<option value={photo._id}>{photo.name}</option>
|
56 |
+
{/each}
|
57 |
+
</select>
|
58 |
+
</label>
|
59 |
+
{/each}
|
60 |
+
</section>
|
61 |
+
{/each}
|
src/routes/admin/pages/[id]/+server.ts
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { collections } from '$lib/server/db';
|
2 |
+
import { pages } from '$lib/server/db/page';
|
3 |
+
import { error } from '@sveltejs/kit';
|
4 |
+
import type { RequestHandler } from './$types';
|
5 |
+
|
6 |
+
export const POST: RequestHandler = async (input) => {
|
7 |
+
const id = input.params.id;
|
8 |
+
|
9 |
+
const body: { type: 'picture' | 'text'; key: string; value: string } = await input.request.json();
|
10 |
+
|
11 |
+
if (!(id in pages)) {
|
12 |
+
throw error(400, 'Mauvaise id de page: ' + id);
|
13 |
+
}
|
14 |
+
|
15 |
+
const type = body.type === 'picture' ? 'pictures' : body.type;
|
16 |
+
|
17 |
+
const page = pages[id as keyof typeof pages];
|
18 |
+
|
19 |
+
if (!(type in page) || !(body.key in page[type])) {
|
20 |
+
throw error(400, 'Mauvaise clé');
|
21 |
+
}
|
22 |
+
|
23 |
+
await collections.pages.updateOne(
|
24 |
+
{ _id: id },
|
25 |
+
{ $set: { [`${type}.${body.key}`]: String(body.value).replaceAll('\r', '') } },
|
26 |
+
{ upsert: true }
|
27 |
+
);
|
28 |
+
|
29 |
+
return new Response();
|
30 |
+
};
|
src/routes/realisations/+page.svelte
CHANGED
@@ -2,6 +2,7 @@
|
|
2 |
import Container from '$lib/components/Container.svelte';
|
3 |
import PictureComponent from '$lib/components/Picture.svelte';
|
4 |
import type { CreationsPage } from '$lib/types/Page';
|
|
|
5 |
import { marked } from 'marked';
|
6 |
import type { PageData } from './$types';
|
7 |
|
@@ -12,7 +13,7 @@
|
|
12 |
|
13 |
type PictureKey = keyof typeof pageData.pictures;
|
14 |
|
15 |
-
const picKeys =
|
16 |
(key) => key.startsWith('realisation-') && pageData.pictures[key as PictureKey]
|
17 |
);
|
18 |
</script>
|
|
|
2 |
import Container from '$lib/components/Container.svelte';
|
3 |
import PictureComponent from '$lib/components/Picture.svelte';
|
4 |
import type { CreationsPage } from '$lib/types/Page';
|
5 |
+
import { typedKeys } from '$lib/utils/typedKeys';
|
6 |
import { marked } from 'marked';
|
7 |
import type { PageData } from './$types';
|
8 |
|
|
|
13 |
|
14 |
type PictureKey = keyof typeof pageData.pictures;
|
15 |
|
16 |
+
const picKeys = typedKeys(pageData.pictures).filter(
|
17 |
(key) => key.startsWith('realisation-') && pageData.pictures[key as PictureKey]
|
18 |
);
|
19 |
</script>
|