diff --git a/README.md b/README.md index fdf0002..a5908ab 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # create-svelte -This is the repo of the website [Kkosmetickysalon.cz](https://kkosmetickysalon.cz), my mom's business, I also did the logos and branding, which are located in `/static` +This is the repo of the website [Kkosmetickysalon.cz](https://kkosmetickysalon.cz), my mom's business, I also did the logos and branding, which are located either in `/static` or are on [Cloudinary](#cloudinary-with-some-scripting) and are printed at multiple places in multiple formats. +The texts are a work of mom, me and Google's Gemini( it's stupid for most complex tasks, but it's good at rewriting text and adding marketing emotions and so on, especially good at Czech unlike the other models) ## Webapp stack @@ -33,9 +34,17 @@ Everything has a schema (e.g. `./sluzby/schema#`) or a type or a generatable tem This implementation offers an option to create or edit json files a [JSON editor](https://github.com/json-editor/json-editor) according to a schema to help write new services in a GUI. The json data is validated by [ajv](https://ajv.js.org) with my script in `./tests/ValidateServices.js/` to match the type Service in `$lib/types/service.d.ts` +### Cloudinary with some scripting + +I automated checking for images in the content folder and uploading them to Cloudinary. +I also have a script that generates a json file with all the images in the content folder and uploads them to Cloudinary. +This json file is then used to get images as simple keys. + ## Features, Components and parts -A list of mostly +### SEO + Opengraph + frontmatter + +my own implementation ### [LibreMaps](https://svelte-maplibre.vercel.app/) @@ -81,7 +90,7 @@ Selfhosting this is the only way. I used ansible and terraform to get this thing ### Email obfuscation -Spammers are hopefully not so smart. I sveltyfied a [SVG email obfuscation method (because JS bad) from https://rouninmedia.github.io/protecting-your-email-address-via-svg-instead-of-js/](https://rouninmedia.github.io/protecting-your-email-address-via-svg-instead-of-js/) +Spammers are hopefully not so smart. I sveltyfied a [SVG email obfuscation method (because JS bad) from https://rouninmedia.github.io/protecting-your-email-address-via-svg-instead-of-js/](https://rouninmedia.github.io/protecting-your-email-address-via-svg-instead-of-js/). ### OpenGraph diff --git a/package.json b/package.json index 0f674dd..fef5f94 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "validate": "pnpm run validate:services && validate:images", "dev": "pnpm run validate && vite dev --mode development", "build": "pnpm run validate && vite build", - "build-dev": "pnpm run validate && vite build --mode development", + "build-dev": "pnpm run vite build --mode development", "preview": "vite preview", "test": "pnpm run test:integration && npm run test:unit", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", @@ -26,7 +26,11 @@ "dependencies": { "@fortawesome/fontawesome-free": "^6.5.1", "@sentry/sveltekit": "^7.107.0", + "@sveltejs/adapter-cloudflare": "^4.4.0", + "@sveltejs/adapter-netlify": "^4.2.0", "@sveltejs/adapter-node": "^5.0.1", + "@sveltejs/adapter-static": "^3.0.1", + "@sveltejs/adapter-vercel": "^5.3.0", "@sveltejs/vite-plugin-svelte": "^3.0.2", "ajv": "^8.12.0" }, diff --git a/src/content/services/dalsi-sluzby/dalsi-sluzby.json b/src/content/services/dalsi-sluzby/dalsi-sluzby.json index f4f3858..56a790e 100644 --- a/src/content/services/dalsi-sluzby/dalsi-sluzby.json +++ b/src/content/services/dalsi-sluzby/dalsi-sluzby.json @@ -8,7 +8,7 @@ "title": "Lifting řas booster (botox)", "description": "Diagnostika pleti, odlíčení tonizace", "id": "lifting-ras-booster", - "image": "", + "image": "1,2,zola", "price": 500, "duration": 60 }, diff --git a/src/content/services/permanentni-make-up/permanentni-make-up.json b/src/content/services/permanentni-make-up/permanentni-make-up.json index 6765b18..45c8d8d 100644 --- a/src/content/services/permanentni-make-up/permanentni-make-up.json +++ b/src/content/services/permanentni-make-up/permanentni-make-up.json @@ -5,23 +5,23 @@ "image": "https://images.unsplash.com/xyz", "services": [ { - "title": "Obočí Pudrové, Ombré", - "description": "Diagnostika pleti, odlíčení tonizace", + "title": "Obočí Pudrové", + "description": "Správně upravené obočí dokáže obličej rozjasnit i omladit. Pokud je vaše obočí příliš světlé / řídké nebo vás prostě jen zdržuje každodenní úprava do požadovaného tvaru, nabízím vám velmi oblíbenou metodu permanentního make-upu – pudrové obočí – která je naprosto skvělým řešením!", "id": "oboci", "image": "", - "price": 3000, + "price": 3500, "duration": 150 }, { - "title": "Horní linky - meziřasové přirozené", + "title": "Meziřasové Linky", "description": "Diagnostika pleti, odlíčení tonizace", "id": "linky", "image": "", - "price": 2000, + "price": 2500, "duration": 120 }, { - "title": "Klasické linky - s ocáskem", + "title": "Klasické linky", "description": "Diagnostika pleti, odlíčení tonizace", "id": "classic-linky", "image": "", @@ -29,15 +29,7 @@ "duration": 150 }, { - "title": "Klasické linky - s ocáskem + spodní linky", - "description": "Diagnostika pleti, odlíčení tonizace", - "id": "classic-linky+spodni", - "image": "", - "price": 3500, - "duration": 150 - }, - { - "title": "Rty - kontura", + "title": "Rty", "description": "Diagnostika pleti, odlíčení tonizace", "id": "rty", "image": "", @@ -45,24 +37,8 @@ "duration": 120 }, { - "title": "3D Rty (kontura a stínování), Full Lips (plné rty)", - "description": "Diagnostika pleti, odlíčení tonizace", - "id": "3d-rty", - "image": "", - "price": 3500, - "duration": 150 - }, - { - "title": "Aquarelle Lips (přirodní stínování, bez kontury)", - "description": "Diagnostika pleti, odlíčení tonizace", - "id": "aquarelle", - "image": "", - "price": 3000, - "duration": 120 - }, - { - "title": "První korekce po aplikaci pmu max. do 3 měsíců", - "description": "Diagnostika pleti, odlíčení tonizace", + "title": "Korekce", + "description": "Pro docílení perfektního vzhledu vašeho permanentního makeupu jsou potřeba dvě návštěvy. Ideální načasování pro druhou návštěvu je 1-3 měsíce od prvního zákroku.", "id": "korekce", "image": "", "price": 1000, diff --git a/src/content/services/vakuslim/vakuslim.json b/src/content/services/vakuslim/vakuslim.json index ca53819..d14cce0 100644 --- a/src/content/services/vakuslim/vakuslim.json +++ b/src/content/services/vakuslim/vakuslim.json @@ -7,7 +7,7 @@ { "title": "Ošetření horních končetin", "description": "Diagnostika pleti, odlíčení tonizace", - "id": "vakuslim-48-zestihlujici-procedura-horni-koncetiny", + "id": "horni-koncetiny", "image": "", "price": 600, "duration": 120 @@ -15,7 +15,7 @@ { "title": "1 ošetření spodní části těla (břicho, boky, dolní končetiny)", "description": "Diagnostika pleti, odlíčení tonizace", - "id": "vakuslim-48-zestihlujici-procedura-spodni-cast-tela", + "id": "spodni-cast-tela", "image": "", "price": 800, "duration": 120 @@ -23,15 +23,15 @@ { "title": "1 ošetření komplet horní-dolní části", "description": "Diagnostika pleti, odlíčení tonizace", - "id": "vakuslim-48-zestihlujici-procedura-komplet-horni-dolni-cast", + "id": "komplet-horni-dolni-cast", "image": "", "price": 1200, "duration": 120 }, { - "title": "6 ošetření předplatné kompet", + "title": "6 ošetření předplatné komplet", "description": "Diagnostika pleti, odlíčení tonizace", - "id": "vakuslim-48-zestihlujici-procedura-6-o-setreni-predplatne-kompet", + "id": "6-o-setreni-predplatne-kompet", "image": "", "price": 6600, "duration": 120 @@ -39,7 +39,7 @@ { "title": "12 ošetření předplatné komplet", "description": "Diagnostika pleti, odlíčení tonizace", - "id": "vakuslim-48-zestihlujici-procedura-12-o-setreni-predplatne-komplet", + "id": "12-o-setreni-predplatne-komplet", "image": "", "price": 11000, "duration": 120 diff --git a/src/content/services/visage/svatebni liceni/svatebni-liceni.md b/src/content/services/visage/svatebni liceni/svatebni-liceni.md new file mode 100644 index 0000000..a2f39a2 --- /dev/null +++ b/src/content/services/visage/svatebni liceni/svatebni-liceni.md @@ -0,0 +1,35 @@ +# Svatebni Liceni + +Vytvořím Vám jemné, stylové líčení na svatbu a nemusíte se bát ani slziček, protože voděodolný make-up vydrží naprosto vše (a na pohled bude stále decentní), ale přesto dostatečně výrazný jak na fotkách, tak na videu. Cílem mé práce je, aby pleť vypadala přirozeně a svěže po celý svatební den. + +Abyste se ve svůj den D cítila krásně, je třeba začít se správnou péčí o pleť minimálně 3 měsíce před svatbou. Pokud péči o pleť nebudete věnovat pozornost, ani dokonalý svatební make-up vám nezaručí vysněný vzhled, ba naopak, může i uškodit a vypadat nepřirozeně. + +## KROKY PŘÍPRAVY + +Nevěsta by měla absolvovat minimálně jednu zkoušku, která by měla proběhnout nejpozději týden před obřadem. + +Nejdříve VÁM zhodnotím typ pleti, tvar obličeje, tvar očí, úst a následně doporučím a vyzkoušíme make-up přímo na míru. Někomu sluší spíš takzvaný „nude“ a někomu zase naopak výraznější líčení. Díky této zkoušce klientka bude vědět v jakém make-upu se cítí nejlépe a doladí se všechny detaily. + +Milé budoucí nevěsty, přineste si s sebou na fotce také svatební šaty a kytici. Je totiž důležité, aby make-up ladil s celkovým stylem a barvou svatby. + +## PRŮBĚH V DEN D (SVATEBNÍ DEN) + +Ve svatební den nejprve pleť správně připravíme pomocí kosmetických přípravků (vyčištění, hydratace, tonikum, hydratační sérum, pleťový krém) dále make-upu, zakryje drobné nedokonalosti korektorem, přepudruje se pleť a pomocí konturování podtrhne Vaší krásu. Dále pomocí stínů zvýrazní oči a pokud má nevěsta zájem, lze nalepit i řasy. Na závěr se vybere vhodný odstín rtěnky, make-up se zafixuje a vy si můžete jít naplno užít Váš den. + +## Cenik svatebního líčení + +Zkouška svatebního líčení trvá přibližně 60-90 minut a stojí 990 Kč. + +Zkouška účesu 300,- + +Svatební den líčení 1500,- + +Svatební den účes 400,- + +Možná domluva prodloužení a zahuštění vlasů metodou keratin (pásky) cena se odvíjí od hustoty (používám pásky 100% human hair, levnější varianta 60-80% mix lidské+sintet. Potřebná domluva předem. + +Ceník human hair 100% 20pásků 3500,- + +Ceník human+syntetika 20pásků 1500,- + +Ceník sundání pramenů do 30min 350,- diff --git a/src/content/services/visage/svatebni liceni/vecerni a plesove liceni.md b/src/content/services/visage/svatebni liceni/vecerni a plesove liceni.md new file mode 100644 index 0000000..e3b3d1e --- /dev/null +++ b/src/content/services/visage/svatebni liceni/vecerni a plesove liceni.md @@ -0,0 +1,17 @@ +# VEČERNÍ + PLESOVÉ LÍČENÍ + +Zahrnuje: báze, korektory, velká modelace obličeje, make-up, pudr, stíny, linky, řasy, obočí, líčka, rozjasňovače, fixátor a aplikace umelých řas (trvanlivost na noc), popř. účes. + +60-90min. 990,- + +120min. líčení + účes 1500,- + +## Prodloužení vlasů + +Možná domluva prodloužení a zahuštění vlasů metodou keratin (pásky) cena se odvíjí od hustoty (používám pásky 100% human hair, levnější varianta 60-80% mix lidské+syntet. Potřebná domluva předem. + +Ceník human hair 100% 20pásků 4500,- + +Ceník human+syntetika 20pásků 1500,- + +Ceník sundání pramenů do 30min 350,- diff --git a/src/content/services/visage/vizazistika.json b/src/content/services/visage/vizazistika.json new file mode 100644 index 0000000..b28784f --- /dev/null +++ b/src/content/services/visage/vizazistika.json @@ -0,0 +1,24 @@ +{ + "title": "Vizážistika", + "description": "Nabízím Vám líčení a účes současně, ušetřete svůj čas o svatebním dni :-), večerní a plesové líčení, základní denní líčení.", + "id": "vizazistika", + "image": "https://images.unsplash.com/xyz", + "services": [ + { + "title": "Svatební Líčení", + "description": "Vytvořím Vám jemné, stylové líčení na svatbu a nemusíte se bát ani slziček, protože voděodolný make-up vydrží naprosto vše (a na pohled bude stále decentní), ale přesto dostatečně výrazný jak na fotkách, tak na videu. Cílem mé práce je, aby pleť vypadala přirozeně a svěže po celý svatební den.", + "id": "svatebni", + "image": "oboci1.png", + "price": 3500, + "duration": 150 + }, + { + "title": "Večerní & Plesové Líčení", + "description": "Diagnostika pleti, odlíčení tonizace", + "id": "vecerni", + "image": "", + "price": 2500, + "duration": 120 + } + ] +} diff --git a/src/content/services/visage/vizážistika.md b/src/content/services/visage/vizážistika.md new file mode 100644 index 0000000..607495d --- /dev/null +++ b/src/content/services/visage/vizážistika.md @@ -0,0 +1,5 @@ +# VIZÁŽISTIKA + +Nabízím Vám líčení a účes současně, ušetřete svůj čas o svatebním dni :-), večerní a plesové líčení, základní denní líčení. + +Práci vizážistky se věnuji po absolvování profesionální školy Make- up institute. Mám osobitý přístup a Vaše spokojenost mě motivuje k lepším výsledkům. Kromě služeb vizážistiky si Vás mohu připravit předem i kosmetickým ošetřením, do salonu mi dojíždí i manikérka. Spolupracuji s profesionální fotografkou, možná domluva focení. diff --git a/src/routes/o-mne/+page.svelte b/src/routes/o-mne/+page.svelte index 220a4ef..5e0dc72 100644 --- a/src/routes/o-mne/+page.svelte +++ b/src/routes/o-mne/+page.svelte @@ -1,3 +1,23 @@ + +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
diff --git a/src/routes/sentry-example/+page.svelte b/src/routes/sentry-example/+page.svelte deleted file mode 100644 index 06712dc..0000000 --- a/src/routes/sentry-example/+page.svelte +++ /dev/null @@ -1,97 +0,0 @@ - - - - -
- - Sentry Onboarding - - - -
-

- - - -

-

- Get Started with this simple Example: -

- -

1. Send us a sample error:

- - -

- 2. Look for the error on the - Issues Page. -

-

- For more information, take a look at the - - Sentry SvelteKit Documentation - -

-
-
- - diff --git a/src/routes/sentry-example/+server.js b/src/routes/sentry-example/+server.js deleted file mode 100644 index beec959..0000000 --- a/src/routes/sentry-example/+server.js +++ /dev/null @@ -1,6 +0,0 @@ -// This is just a very simple API route that throws an example error. -// Feel free to delete this file and the entire sentry route. - -export const GET = async () => { - throw new Error("Sentry Example API Route Error"); -}; diff --git a/src/routes/sluzby/[categoryId]/[serviceId]/+page.svelte b/src/routes/sluzby/[categoryId]/[serviceId]/+page.svelte index ab9e9eb..95e1896 100644 --- a/src/routes/sluzby/[categoryId]/[serviceId]/+page.svelte +++ b/src/routes/sluzby/[categoryId]/[serviceId]/+page.svelte @@ -10,3 +10,4 @@ + \ No newline at end of file diff --git a/svelte.config.js b/svelte.config.js index b588d9c..26801ec 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,42 +1,69 @@ -import adapter from '@sveltejs/adapter-node'; -import preprocess from 'svelte-preprocess'; +// svelte adapter -// config extensions -import { mdsvex } from 'mdsvex'; -import mdsvexConfig from './mdsvex.config.js'; +import adapterNode from '@sveltejs/adapter-node' +import adapterVercel from '@sveltejs/adapter-vercel' +import adapterNetlify from '@sveltejs/adapter-netlify' +import adapterCloudflare from '@sveltejs/adapter-cloudflare' +import adapterStatic from '@sveltejs/adapter-static' +// svelte preprocessor +import { mdsvex } from 'mdsvex' +import mdsvexConfig from './mdsvex.config.js' +// import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' +import preprocess from 'svelte-preprocess' -/** @type {import('@sveltejs/kit').Config} */ -const config = { - extensions: ['.svelte', ...(mdsvexConfig.extensions || [])], - // Consult https://github.com/sveltejs/svelte-preprocess - // for more information about preprocessors - preprocess: [ - preprocess({ - postcss: true - }), - mdsvex(mdsvexConfig) - ], - kit: { - adapter: adapter({ - out: 'build', - precompress: false - }), +function getAdapter() { + if (Object.keys(process.env).some(key => key.includes('VERCEL'))) { + return adapterVercel() + } else if (Object.keys(process.env).some(key => key.includes('NETLIFY'))) { + return adapterNetlify() + } else if (Object.keys(process.env).some(key => key.includes('CF_PAGES'))) { + return adapterCloudflare() + } else { + return process.env.ADAPTER === 'node' + ? adapterNode({ out: 'build' }) + : adapterStatic({ + pages: 'build', + assets: 'build', + fallback: undefined + }) + } +} - // Aliases need tsconfig explicit inclusion - alias: { - $lib: './src/lib', - $root: './', - $src: './src', - $routes: './src/routes', - $content: './src/content' - }, - env: { - publicPrefix: "PUBLIC_", - }, - - // https://kit.svelte.dev/docs/configuration#alias - }, - -}; +/** @type {import("@svletejs/kit".Config)} */ +export default { + extensions: ['.svelte', ...(mdsvexConfig.extensions || [])], + preprocess: [preprocess({ postcss: true }), mdsvex(mdsvexConfig) /*, vitePreprocess()*/], + vitePlugin: { + inspector: true + }, + kit: { + adapter: getAdapter(), + alias: { + $lib: './src/lib', + $root: './', + $src: './src', + $routes: './src/routes', + $content: './content' + }, + csrf: { + checkOrigin: process.env.NODE_ENV === 'development' ? false : true + }, + prerender: { + crawl: true, + handleMissingId: 'warn', + handleHttpError: ({ status, path, referrer, referenceType, message }) => { + // Handle blog trying to prerender relative links that it can't + if ( + (status == 404 && path.startsWith('/blog')) || + path.startsWith('/projects') || + (path.startsWith('/') && referenceType == 'linked') + ) { + console.warn(`PRERENDER ignored route ${path}`) + return + } -export default config; + throw new Error(`${status} ${path} from ${referrer}, ~~~~~~~~~ message: ${message}~~~~~~~~~`) + } + } + } +} diff --git a/tests/validateImages.js b/tests/validateImages.js index 43d699b..59d4515 100644 --- a/tests/validateImages.js +++ b/tests/validateImages.js @@ -11,21 +11,24 @@ cloudinary.v2.config({ async function validateImages() { const invalidImages = []; - - for (const publicId in imagesData) { - try { - // Check if the image exists on Cloudinary - await cloudinary.v2.api.resource(publicId); - } catch (error) { - invalidImages.push(publicId); + try { + for (const publicId in imagesData) { + try { + // Check if the image exists on Cloudinary + await cloudinary.v2.api.resource(publicId); + } catch (error) { + invalidImages.push(publicId); + } } - } - if (invalidImages.length > 0) { - console.error(`The following images are missing or invalid on Cloudinary: ${invalidImages.join(', ')}`); - process.exit(1); - } else { - console.log('All images are valid on Cloudinary.'); + if (invalidImages.length > 0) { + console.error(`The following images are missing or invalid on Cloudinary: ${invalidImages.join(', ')}`); + process.exit(1); + } else { + console.log('All images are valid on Cloudinary.'); + } + } catch (error) { + console.error(error); } } diff --git a/vite.config.ts b/vite.config.ts index 3c18393..6546e2d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,6 +8,9 @@ export default defineConfig({ server: { host: 'localhost', port: 5174, + fs: { + allow: ['..'], + } }, envPrefix: "PUBLIC_", plugins: [sentrySvelteKit({ @@ -34,10 +37,11 @@ export default defineConfig({ }, resolve: { alias: { - $lib: path.resolve(__dirname, 'src', 'lib'), - $root: path.resolve(__dirname), - $src: path.resolve(__dirname, 'src'), - $routes: path.resolve(__dirname, 'src', 'routes') + $lib: path.resolve('.', 'src/lib'), + $root: path.resolve('.'), + $src: path.resolve('.', 'src'), + $routes: path.resolve('.', 'src/routes'), + $content: path.resolve('.', 'src/content'), } } });