diff options
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> |
