diff options
| author | ivarlovlie <git@ivarlovlie.no> | 2023-02-21 23:24:14 +0100 |
|---|---|---|
| committer | ivarlovlie <git@ivarlovlie.no> | 2023-02-21 23:24:14 +0100 |
| commit | 6cb399e7267ae78e3e498bdbf5f51678ffb2cd45 (patch) | |
| tree | b109832a208927821fcfe65bd98ff9e3f391c44c /src | |
| parent | 54bbc06bd84437c6b38e2f6c57060f21a8318720 (diff) | |
| download | auroraklinikken.no-6cb399e7267ae78e3e498bdbf5f51678ffb2cd45.tar.xz auroraklinikken.no-6cb399e7267ae78e3e498bdbf5f51678ffb2cd45.zip | |
feat: Many things
Configure sanity in same project as the app
Implement type safe sanity schema
Read localised documents
Strip down design
Diffstat (limited to 'src')
22 files changed, 298 insertions, 109 deletions
diff --git a/src/components/card-v4.svelte b/src/components/card-v4.svelte index ff35492..fccd423 100644 --- a/src/components/card-v4.svelte +++ b/src/components/card-v4.svelte @@ -16,16 +16,13 @@ <PortableText value={description} /> {/if} </p> - <div class="flex flex-wrap gap-3"> - <a href="#0" class="btn btn--subtle">Learn more</a> - <a href="#0" class="btn btn--primary">Buy</a> - </div> + <slot /> </div> </div> <style lang="postcss"> .card { - @apply flex flex-col bg-floor rounded-xl overflow-hidden; + @apply flex flex-col bg-floor rounded-xl overflow-hidden h-max; box-shadow: 0 0 0 1px hsla(230, 13%, 9%, 0.05), 0 0.3px 0.4px hsla(230, 13%, 9%, 0.02), 0 0.9px 1.5px hsla(230, 13%, 9%, 0.045), 0 3.5px 6px hsla(230, 13%, 9%, 0.09); } diff --git a/src/components/locale-switcher.svelte b/src/components/locale-switcher.svelte index 938d18a..aa4e0c4 100644 --- a/src/components/locale-switcher.svelte +++ b/src/components/locale-switcher.svelte @@ -35,12 +35,8 @@ <svelte:window on:popstate={handlePopStateEvent} /> -<ul> - {#each locales as l} - <li> - <a class:active={l === $locale} href={`${replaceLocaleInUrl($page.url, l)}`}> - {l} - </a> - </li> - {/each} -</ul> +{#if $locale == "en"} + <a href={replaceLocaleInUrl($page.url, "nb")}>Norsk Bokmål</a> +{:else if $locale == "nb"} + <a href={replaceLocaleInUrl($page.url, "en")}>English</a> +{/if} diff --git a/src/i18n/en/index.ts b/src/i18n/en/index.ts index a6b79c2..1f8260f 100644 --- a/src/i18n/en/index.ts +++ b/src/i18n/en/index.ts @@ -7,6 +7,8 @@ const en = { emailTitle: "Email", phoneTitle: "Phone", }, + goToBookingPage: "Go to booking", + ourServices: "Our services", homeTitle: "Home", } satisfies BaseTranslation; diff --git a/src/i18n/i18n-types.ts b/src/i18n/i18n-types.ts index 9a32b2f..52b1fd2 100644 --- a/src/i18n/i18n-types.ts +++ b/src/i18n/i18n-types.ts @@ -33,6 +33,14 @@ type RootTranslation = { phoneTitle: string } /** + * Go to booking + */ + goToBookingPage: string + /** + * Our services + */ + ourServices: string + /** * Home */ homeTitle: string @@ -58,6 +66,14 @@ export type TranslationFunctions = { phoneTitle: () => LocalizedString } /** + * Go to booking + */ + goToBookingPage: () => LocalizedString + /** + * Our services + */ + ourServices: () => LocalizedString + /** * Home */ homeTitle: () => LocalizedString diff --git a/src/i18n/nb/index.ts b/src/i18n/nb/index.ts index 5206b07..0064639 100644 --- a/src/i18n/nb/index.ts +++ b/src/i18n/nb/index.ts @@ -7,6 +7,8 @@ const nb = { emailTitle: "E-postadresse", phoneTitle: "Telefon", }, + goToBookingPage: "Gå til bestilling", + ourServices: "Våre tjenester", homeTitle: "Hjem", } satisfies Translation; diff --git a/src/lib/sanity-client.ts b/src/lib/sanity/client.ts index 7aa868b..7aa868b 100644 --- a/src/lib/sanity-client.ts +++ b/src/lib/sanity/client.ts diff --git a/src/lib/sanity/locales.ts b/src/lib/sanity/locales.ts new file mode 100644 index 0000000..c3280f4 --- /dev/null +++ b/src/lib/sanity/locales.ts @@ -0,0 +1,5 @@ +export const supportedLanguages = [ + { id: "en", title: "English" }, + { id: "nb", title: "Norsk Bokmål", isDefault: true }, +]; +export const baseLanguage = supportedLanguages.find((l) => l.isDefault) as { id: string; title: string, isDefault: boolean }; diff --git a/src/lib/sanity/schemas/default/contact.ts b/src/lib/sanity/schemas/default/contact.ts new file mode 100644 index 0000000..03b1942 --- /dev/null +++ b/src/lib/sanity/schemas/default/contact.ts @@ -0,0 +1,36 @@ +import { s } from "sanity-typed-schema-builder"; + +export default s.document({ + name: "contact", + title: "Contact section", + // @ts-ignore + i18n: true, + fields: [ + { + title: "Address lines", + name: "addressLines", + type: s.array({ + of: [s.string()] + }), + optional: true + }, + { + title: "Email", + name: "email", + type: s.string(), + optional: true, + }, + { + title: "Phone", + name: "phone", + type: s.string(), + optional: true, + }, + { + title: "Phone hours", + name: "phoneHours", + type: s.string(), + optional: true, + } + ], +});
\ No newline at end of file diff --git a/src/lib/sanity/schemas/default/description.ts b/src/lib/sanity/schemas/default/description.ts new file mode 100644 index 0000000..3c5cbfa --- /dev/null +++ b/src/lib/sanity/schemas/default/description.ts @@ -0,0 +1,22 @@ +import { s } from "sanity-typed-schema-builder"; + +export default s.document({ + name: "description", + // @ts-ignore + i18n: true, + title: "Description section", + fields: [ + { + title: "Title", + name: "title", + type: s.string(), + }, + { + title: "Content", + name: "content", + type: s.array({ + of: [s.block()] + }) + }, + ], +}); diff --git a/src/lib/sanity/schemas/default/faq.ts b/src/lib/sanity/schemas/default/faq.ts new file mode 100644 index 0000000..6fb9e71 --- /dev/null +++ b/src/lib/sanity/schemas/default/faq.ts @@ -0,0 +1,22 @@ +import { s } from "sanity-typed-schema-builder"; + +export default s.document({ + name: "faq", + title: "FAQ section", + // @ts-ignore + i18n: true, + fields: [ + { + name: "title", + title: "Question", + type: s.string(), + }, + { + name: "answer", + title: "Answer", + type: s.array({ + of: [s.block()] + }), + } + ] +})
\ No newline at end of file diff --git a/src/lib/sanity/schemas/default/hero.ts b/src/lib/sanity/schemas/default/hero.ts new file mode 100644 index 0000000..df7b8ae --- /dev/null +++ b/src/lib/sanity/schemas/default/hero.ts @@ -0,0 +1,28 @@ +import { s } from "sanity-typed-schema-builder"; + +export default s.document({ + name: "hero", + title: "Hero section", + // @ts-ignore + i18n: true, + fields: [ + { + title: "Title", + name: "title", + type: s.string(), + }, + { + title: "Content", + name: "content", + type: s.array({ + of: [s.block()] + }), + }, + { + title: "Image", + name: "image", + type: s.image(), + optional: true + }, + ], +}); diff --git a/src/lib/sanity/schemas/default/index.ts b/src/lib/sanity/schemas/default/index.ts new file mode 100644 index 0000000..4befb4d --- /dev/null +++ b/src/lib/sanity/schemas/default/index.ts @@ -0,0 +1,16 @@ +import contact from "./contact"; +import description from "./description"; +import faq from "./faq"; +import hero from "./hero"; +import product from "./product"; + +export default { + name: "default-schema", + types: [ + contact.schema(), + product.schema(), + description.schema(), + faq.schema(), + hero.schema() + ] +}
\ No newline at end of file diff --git a/src/lib/sanity/schemas/default/product.ts b/src/lib/sanity/schemas/default/product.ts new file mode 100644 index 0000000..92db714 --- /dev/null +++ b/src/lib/sanity/schemas/default/product.ts @@ -0,0 +1,37 @@ +import { s } from "sanity-typed-schema-builder"; + +export default s.document({ + name: "product", + title: "Products", + // @ts-ignore + i18n: true, + fields: [ + { + name: "title", + title: "Title", + type: s.string(), + }, + { + name: "duration", + title: "Duration", + type: s.string() + }, + { + name: "cost", + title: "Cost", + type: s.string() + }, + { + name: "description", + title: "Description", + type: s.array({ + of: [s.block()] + }) + }, + { + name: "orderLink", + title: "Link to booking", + type: s.url() + }, + ], +});
\ No newline at end of file diff --git a/src/lib/sanity/types/block-array.ts b/src/lib/sanity/types/block-array.ts new file mode 100644 index 0000000..28ca0c4 --- /dev/null +++ b/src/lib/sanity/types/block-array.ts @@ -0,0 +1,9 @@ +export type SanityBlockArray = { + _type: string; + children: any[]; + markDefs?: any[] | undefined; + style?: string | undefined; + listItem?: string | undefined; + level?: number | undefined; + _key: string; +}[]
\ No newline at end of file diff --git a/src/lib/utils.ts b/src/lib/utils.ts index f7c040d..1dc98f0 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -14,14 +14,4 @@ export const replaceLocaleInUrl = (url: URL, locale: string, full = false): stri const newUrl = new URL(url.toString()); newUrl.pathname = new_pathname; return newUrl.toString(); -}; - -export function fromLocalizedString(localizedString: string | object, locale: Locales) { - if (typeof localizedString === "string") return localizedString; - // @ts-ignore - if (localizedString[locale]) return localizedString[locale]; - // @ts-ignore - if (localizedString["nb"]) return localizedString["nb"]; - // @ts-ignore - if (localizedString["en"]) return localizedString["en"]; -} +};
\ No newline at end of file diff --git a/src/routes/[lang=lang]/+page.server.ts b/src/routes/[lang=lang]/+page.server.ts index c2284ee..deafae3 100644 --- a/src/routes/[lang=lang]/+page.server.ts +++ b/src/routes/[lang=lang]/+page.server.ts @@ -1,22 +1,31 @@ -import { sanity } from "$lib/sanity-client"; +import { sanity } from "$lib/sanity/client"; import type { PageServerLoad } from "./$types"; import groq from "groq"; import type { ContactModel } from "./sections/contact.svelte"; -import { fromLocalizedString } from "$lib/utils"; import type { HeroModel } from "./sections/hero.svelte"; import type { DescriptionModel } from "./sections/description.svelte"; +import type { s } from "sanity-typed-schema-builder"; +import type contactType from "$lib/sanity/schemas/default/contact"; +import type heroType from "$lib/sanity/schemas/default/hero"; +import type descriptionType from "$lib/sanity/schemas/default/description"; +import type productType from "$lib/sanity/schemas/default/product"; +import type { ProductsModel } from "./sections/products.svelte"; export const load = (async ({ locals }) => { - const contactSection = await sanity.fetch(groq`*[_type == "contact"][0]`); - const heroSection = await sanity.fetch(groq`*[_type == "hero"][0]`); - const descriptionSection = await sanity.fetch(groq`*[_type == "description"][0]`); - const products = await sanity.fetch(groq`*[_type == "product"]`); + const commonParams = { + lang: locals.locale + } + const contactSection: s.infer<typeof contactType> = await sanity.fetch(groq`*[_type == "contact" && __i18n_lang == $lang][0]`, { ...commonParams }) ?? {}; + const heroSection: s.infer<typeof heroType> = await sanity.fetch(groq`*[_type == "hero" && __i18n_lang == $lang][0]`, { ...commonParams }) ?? {}; + const descriptionSection: s.infer<typeof descriptionType> = await sanity.fetch(groq`*[_type == "description" && __i18n_lang == $lang][0]`, { ...commonParams }) ?? {}; + const products: Array<s.infer<typeof productType>> = await sanity.fetch(groq`*[_type == "product" && __i18n_lang == $lang]`, { ...commonParams }) ?? []; + return { contact: { - phone: fromLocalizedString(contactSection.phone, locals.locale), - email: fromLocalizedString(contactSection.email, locals.locale), - phoneHours: fromLocalizedString(contactSection.phoneHours, locals.locale), - addressLines: contactSection.addressLines.map((el: string | object) => fromLocalizedString(el, locals.locale)), + phone: contactSection.phone, + email: contactSection.email, + phoneHours: contactSection.phoneHours, + addressLines: contactSection.addressLines?.map((el: string | object) => el) ?? [], } as ContactModel, hero: { title: heroSection.title, @@ -26,6 +35,14 @@ export const load = (async ({ locals }) => { title: descriptionSection.title, content: descriptionSection.content, } as DescriptionModel, - products: products + products: { + products: products.map((p) => ({ + cost: p.cost, + description: p.description, + duration: p.duration, + orderLink: p.orderLink, + title: p.title + })) + } as ProductsModel }; }) satisfies PageServerLoad; diff --git a/src/routes/[lang=lang]/+page.svelte b/src/routes/[lang=lang]/+page.svelte index f2028c6..0ab63ee 100644 --- a/src/routes/[lang=lang]/+page.svelte +++ b/src/routes/[lang=lang]/+page.svelte @@ -3,13 +3,48 @@ import Hero from "./sections/hero.svelte"; import Description from "./sections/description.svelte"; import Products from "./sections/products.svelte"; + import LL from "$i18n/i18n-svelte"; import type { PageData } from "./$types"; export let data: PageData; </script> -<Hero model={data.hero} /> -<Description model={data.description} /> -<Contact model={data.contact} /> -<Products model={data.products} /> +<main> + <section id="hero"> + <div class="hero"> + <Hero model={data.hero} /> + </div> + <div> + <Description model={data.description} /> + </div> + </section> + <section> + <h3 class="mb-3">{$LL.ourServices()}</h3> + <Products model={data.products} /> + </section> + <section> + <Contact model={data.contact} /> + </section> +</main> + +<style> + main { + margin: 0 5vw 2vh 5vw; + display: flex; + flex-direction: column; + } + main section { + margin-bottom: 2vh; + } + + #hero { + display: grid; + grid-template-columns: repeat(2, 50%); + justify-content: space-between; + } + + #hero .hero { + padding: 3.2vw; + } +</style> diff --git a/src/routes/[lang=lang]/sections/contact.svelte b/src/routes/[lang=lang]/sections/contact.svelte index b058180..2898e83 100644 --- a/src/routes/[lang=lang]/sections/contact.svelte +++ b/src/routes/[lang=lang]/sections/contact.svelte @@ -23,9 +23,9 @@ {#if visible} <section class="contact relative z-1"> - <div class="w-[calc(100%_-_2.5rem)] lg:w-[calc(100%_-_4rem)] mx-auto max-w-7xl"> + <div class="mx-auto"> <div class="mb-8 lg:mb-12"> - <h1 class="text-center">{$LL.contact.title()}</h1> + <h3>{$LL.contact.title()}</h3> </div> <div class="grid grid-cols-12 gap-8 lg:gap-12"> diff --git a/src/routes/[lang=lang]/sections/description.svelte b/src/routes/[lang=lang]/sections/description.svelte index 79a3939..1fc23ab 100644 --- a/src/routes/[lang=lang]/sections/description.svelte +++ b/src/routes/[lang=lang]/sections/description.svelte @@ -1,7 +1,9 @@ <script context="module" lang="ts"> + import type { SanityBlockArray } from "$lib/sanity/types/block-array"; + export type DescriptionModel = { title: string; - content?: any; + content?: SanityBlockArray; }; </script> @@ -19,7 +21,7 @@ </script> {#if visible} - <h3>{model.title}</h3> + <h3 class="mb-3">{model.title}</h3> {#if model.content} <PortableText value={model.content} /> {/if} diff --git a/src/routes/[lang=lang]/sections/hero.svelte b/src/routes/[lang=lang]/sections/hero.svelte index 3cdf221..8a874dc 100644 --- a/src/routes/[lang=lang]/sections/hero.svelte +++ b/src/routes/[lang=lang]/sections/hero.svelte @@ -1,7 +1,9 @@ <script context="module" lang="ts"> + import type { SanityBlockArray } from "$lib/sanity/types/block-array"; + export type HeroModel = { title: string; - content?: any; + content?: SanityBlockArray; }; </script> @@ -19,57 +21,8 @@ </script> {#if visible} - <section class="has-section-divider-bottom bg-contrast-low/50"> - <div class="py-20 lg:py-32"> - <div class="w-[calc(100%_-_2.5rem)] lg:w-[calc(100%_-_4rem)] mx-auto max-w-lg md:max-w-3xl"> - <div class="text-component"> - <h1>{model.title}</h1> - {#if model.content} - <PortableText value={model.content} /> - {/if} - </div> - </div> - </div> - - <div class="section-divider"> - <svg viewBox="0 0 1920 60" aria-hidden="true"> - <path - class="fill-floor" - d="M-153.5,85.5a4002.033,4002.033,0,0,1,658-71c262.854-6.5,431.675,15.372,600,27,257.356,17.779,624.828,19.31,1089-58v102Z" - /> - </svg> - </div> - </section> + <h1>{model.title}</h1> + {#if model.content} + <PortableText value={model.content} /> + {/if} {/if} - -<style lang="postcss"> - :root { - --section-divider-width: 1920; - --section-divider-height: 60; - --section-divider-ratio: calc(100% * var(--section-divider-height) / var(--section-divider-width)); - } - - [class*="has-section-divider"] { - position: relative; - } - - .has-section-divider-bottom { - padding-bottom: var(--section-divider-ratio); - } - - .section-divider { - position: absolute; - bottom: -1px; - left: 0; - width: 100%; - overflow: hidden; - } - .section-divider svg { - position: relative; - display: block; - height: auto; - max-width: none; - width: 102%; - left: -1%; - } -</style> diff --git a/src/routes/[lang=lang]/sections/products.svelte b/src/routes/[lang=lang]/sections/products.svelte index 816e276..a2999dc 100644 --- a/src/routes/[lang=lang]/sections/products.svelte +++ b/src/routes/[lang=lang]/sections/products.svelte @@ -1,4 +1,5 @@ <script context="module" lang="ts"> + import type { SanityBlockArray } from "$lib/sanity/types/block-array"; export type ProductsModel = { products: ProductModel[]; }; @@ -7,13 +8,14 @@ title: string; duration: string; cost: string; - description: string; + description: SanityBlockArray; orderLink: string; }; </script> <script lang="ts"> import CardV4 from "$components/card-v4.svelte"; + import LL from "$i18n/i18n-svelte"; export let model: ProductsModel; @@ -28,14 +30,19 @@ {#if visible} <div class="wrapper"> {#each model.products as product} - <CardV4 description={product.description} title={product.title} /> + <CardV4 description={product.description} title={product.title}> + <div class="flex flex-wrap justify-end align-bottom"> + <a href={product.orderLink} class="btn btn--primary">{$LL.goToBookingPage()}</a> + </div> + </CardV4> {/each} </div> {/if} <style lang="postcss"> .wrapper { - display: grid; - grid-template-columns: repeat(50%); + display: flex; + flex-direction: row; + gap: 1em } </style> diff --git a/src/routes/parts/header.svelte b/src/routes/parts/header.svelte index 55f1da0..4be4b21 100644 --- a/src/routes/parts/header.svelte +++ b/src/routes/parts/header.svelte @@ -1,12 +1,9 @@ <script lang="ts"> - import { locale } from "$i18n/i18n-svelte"; import LocaleSwitcher from "$components/locale-switcher.svelte"; </script> -<header> - <a href="/{$locale}"> - <h1>Auroraklinikken</h1> - </a> - - <LocaleSwitcher /> +<header class="flex"> + <div class="flex justify-end"> + <LocaleSwitcher /> + </div> </header> |
