Skip to content

Commit

Permalink
feat: add single product page (#562)
Browse files Browse the repository at this point in the history
Co-authored-by: moripen <107696180+moripen@users.noreply.github.com>
  • Loading branch information
simenandre and moripen committed Jun 19, 2023
1 parent 8ff886c commit 62d8f2d
Show file tree
Hide file tree
Showing 12 changed files with 399 additions and 79 deletions.
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
engine-strict=true
resolution-mode=highest
auto-install-peers=true
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
"type": "module",
"dependencies": {
"@fontsource-variable/sora": "^5.0.1",
"@portabletext/svelte": "^2.0.0",
"@sanity/client": "^6.1.2",
"@sanity/image-url": "^1.0.2",
"sass": "^1.62.1",
"svelte-preprocess": "^5.0.4",
"zod": "^3.21.4"
Expand Down
32 changes: 32 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions src/components/inline-image.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import type { CustomBlockComponentProps } from '@portabletext/svelte';
import { urlFor } from '../data/sanity-client-browser';
export let portableText: CustomBlockComponentProps<{ asset: { ref: string } }>;
const url = urlFor(portableText.value.asset).url();
</script>

<img src={url} alt={portableText.value} />
59 changes: 59 additions & 0 deletions src/data/projects/get-projects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { z } from 'zod';
import { client } from '../sanity-client';
import { projectModel, type Project } from './types';

const query = `
*[
_type == "project" &&
!(_id in path("drafts.**"))
] | order(yearFrom desc) {
_id,
_type,
name,
title,
description,
preamble,
mission,
image,
body,
slug,
customer->{
name,
privacy {
hideName,
anonymizedName
},
sameAs
},
yearFrom,
yearTo,
technologies[]->{
name
},
links[]{
_type, name, target, url
}
}
`;

export async function getProjects(): Promise<Project[]> {
const dataFromSanity = await client.fetch(query);
const projects = z
.array(projectModel)
.parse(dataFromSanity)
.map((project) => {
const customer = project.customer?.privacy?.hideName
? {
...project.customer,
name: project.customer?.privacy?.anonymizedName ?? 'Anonym'
}
: project.customer;

return {
...project,
customer
};
});

return projects;
}
54 changes: 54 additions & 0 deletions src/data/projects/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { z } from 'zod';

export const privacyModel = z.object({
hideName: z.boolean(),
anonymizedName: z.string().nullable()
});

export const customerModel = z.object({
name: z.string(),
privacy: privacyModel.nullable(),
sameAs: z.array(z.string()).nullable()
});

export const projectModel = z.object({
_type: z.literal('project'),
name: z.string(),
title: z.string().nullable(),
preamble: z.any().nullable(),
mission: z.string().nullable(),
image: z
.object({
asset: z.object({
_ref: z.string()
})
})
.nullable(),
body: z.any().nullable(),
description: z.string(),
customer: customerModel.nullable(),
yearFrom: z.number(),
yearTo: z.number().nullable(),
slug: z.object({
current: z.string()
}),
technologies: z
.array(
z.object({
name: z.string()
})
)
.nullable(),
links: z
.array(
z.object({
_type: z.literal('link'),
name: z.string(),
target: z.string(),
url: z.string()
})
)
.nullable()
});

export type Project = z.infer<typeof projectModel>;
15 changes: 15 additions & 0 deletions src/data/sanity-client-browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import imageUrlBuilder from '@sanity/image-url';
import type { SanityImageSource } from '@sanity/image-url/lib/types/types';

// Get a pre-configured url-builder from your sanity client
const builder = imageUrlBuilder({
projectId: '1zkk2ha0',
dataset: 'production'
});

// Then we like to make a simple function like this that gives the
// builder an image and returns the builder for you to specify additional
// parameters:
export function urlFor(source: SanityImageSource) {
return builder.image(source);
}
12 changes: 12 additions & 0 deletions src/data/sanity-client.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { createClient } from '@sanity/client';
import imageUrlBuilder from '@sanity/image-url';
import type { SanityImageSource } from '@sanity/image-url/lib/types/types';

export const client = createClient({
projectId: '1zkk2ha0',
Expand All @@ -7,3 +9,13 @@ export const client = createClient({
token: process.env.SANITY_TOKEN,
apiVersion: '2023-05-29'
});

// Get a pre-configured url-builder from your sanity client
const builder = imageUrlBuilder(client);

// Then we like to make a simple function like this that gives the
// builder an image and returns the builder for you to specify additional
// parameters:
export function urlFor(source: SanityImageSource) {
return builder.image(source);
}
81 changes: 2 additions & 79 deletions src/routes/(default)/projects/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,85 +1,8 @@
import { z } from 'zod';
import { client } from '../../../data/sanity-client';
import { getProjects } from '../../../data/projects/get-projects';

export const prerender = true;

const query = `
*[
_type == "project" &&
!(_id in path("drafts.**"))
] | order(yearFrom desc) {
_type,
name,
description,
customer->{
name,
privacy {
hideName,
anonymizedName
},
sameAs
},
yearFrom,
yearTo,
technologies[]->{
name
},
links[]{
_type, name, target, url
}
}
`;

const privacy = z.object({
hideName: z.boolean(),
anonymizedName: z.string().nullable()
});

const customer = z.object({
name: z.string(),
privacy: privacy.nullable(),
sameAs: z.array(z.string()).nullable()
});

const model = z.object({
_type: z.literal('project'),
name: z.string(),
description: z.string(),
customer: customer.nullable(),
yearFrom: z.number(),
yearTo: z.number().nullable(),
links: z
.array(
z.object({
_type: z.literal('link'),
name: z.string(),
target: z.string(),
url: z.string()
})
)
.nullable()
});

export type Project = z.infer<typeof model>;

export const load = async () => {
const dataFromSanity = await client.fetch(query);
const projects = z
.array(model)
.parse(dataFromSanity)
.map((project) => {
const customer = project.customer?.privacy?.hideName
? {
...project.customer,
name: project.customer?.privacy?.anonymizedName ?? 'Anonymt'
}
: project.customer;

return {
...project,
customer
};
});

const projects = await getProjects();
return { projects };
};
38 changes: 38 additions & 0 deletions src/routes/(default)/projects/[slug]/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { error } from '@sveltejs/kit';
import { getProjects } from '../../../../data/projects/get-projects';
import { urlFor } from '../../../../data/sanity-client';
import type { EntryGenerator } from './$types.js';

const projects = await getProjects();

export const prerender = true;

export const entries = (() => {
const urls = projects
.filter((project) => project.preamble)
.map((project) => ({
slug: project.slug.current
}));

return urls;
}) satisfies EntryGenerator;

export const load = async ({ params }) => {
const project = projects.find((project) => project.slug.current === params.slug);

if (!project) {
throw error(404, {
message: 'Not found'
});
}

return {
project: {
...project,
title: project.title ?? project.name,
imageUrl: project.image ? urlFor(project.image.asset).url() : undefined,
// TODO: Add imageAlt from Sanity
imageAlt: project.name
}
};
};
Loading

0 comments on commit 62d8f2d

Please sign in to comment.