Merge pull request #2 from archtika/devel

Add ESLint and loading spinners for requests
This commit is contained in:
Thilo Hohlt
2024-09-07 14:29:27 +02:00
committed by GitHub
19 changed files with 1907 additions and 25 deletions

36
web-app/eslint.config.js Normal file
View File

@@ -0,0 +1,36 @@
import js from "@eslint/js";
import ts from "typescript-eslint";
import svelte from "eslint-plugin-svelte";
import prettier from "eslint-config-prettier";
import globals from "globals";
/** @type {import('eslint').Linter.Config[]} */
export default [
js.configs.recommended,
...ts.configs.recommended,
...svelte.configs["flat/recommended"],
prettier,
...svelte.configs["flat/prettier"],
{
languageOptions: {
globals: {
...globals.browser,
...globals.node
}
}
},
{
files: ["**/*.svelte"],
languageOptions: {
parserOptions: {
parser: ts.parser
}
},
rules: {
"svelte/no-at-html-tags": "off"
}
},
{
ignores: ["build/", ".svelte-kit/", "dist/"]
}
];

1636
web-app/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,7 @@
"test": "playwright test", "test": "playwright test",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check .", "lint": "prettier --check . && eslint .",
"format": "prettier --write ." "format": "prettier --write ."
}, },
"devDependencies": { "devDependencies": {
@@ -18,12 +18,20 @@
"@sveltejs/adapter-node": "5.2.2", "@sveltejs/adapter-node": "5.2.2",
"@sveltejs/kit": "2.5.22", "@sveltejs/kit": "2.5.22",
"@sveltejs/vite-plugin-svelte": "3.1.1", "@sveltejs/vite-plugin-svelte": "3.1.1",
"@types/eslint": "9.6.1",
"@types/eslint__js": "8.42.3",
"@types/eslint-config-prettier": "6.11.3",
"@types/node": "22.2.0", "@types/node": "22.2.0",
"eslint": "9.10.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-svelte": "2.43.0",
"globals": "15.9.0",
"prettier": "3.3.3", "prettier": "3.3.3",
"prettier-plugin-svelte": "3.2.6", "prettier-plugin-svelte": "3.2.6",
"svelte": "5.0.0-next.220", "svelte": "5.0.0-next.220",
"svelte-check": "3.8.5", "svelte-check": "3.8.5",
"typescript": "5.5.4", "typescript": "5.5.4",
"typescript-eslint": "8.4.0",
"vite": "5.4.0" "vite": "5.4.0"
}, },
"type": "module", "type": "module",

View File

@@ -0,0 +1,32 @@
<div class="spinner"></div>
<style>
@keyframes spinner {
to {
transform: rotate(360deg);
}
}
.spinner {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 40;
}
.spinner::before {
content: "";
position: absolute;
inline-size: 4rem;
block-size: 4rem;
inset-block-start: 50%;
inset-inline-start: 50%;
margin-block-start: -2rem;
margin-inline-start: -2rem;
border-radius: 50%;
border: var(--border-primary);
border-width: 0.125rem;
border-block-start-color: var(--color-accent);
animation: spinner 0.6s linear infinite;
}
</style>

View File

@@ -6,13 +6,6 @@ import GithubSlugger from "github-slugger";
import DOMPurify from "isomorphic-dompurify"; import DOMPurify from "isomorphic-dompurify";
import { applyAction, deserialize } from "$app/forms"; import { applyAction, deserialize } from "$app/forms";
export const sortOptions = [
{ value: "creation-time", text: "Creation time" },
{ value: "last-modified", text: "Last modified" },
{ value: "title-a-to-z", text: "Title - A to Z" },
{ value: "title-z-to-a", text: "Title - Z to A" }
];
export const ALLOWED_MIME_TYPES = ["image/jpeg", "image/png", "image/svg+xml", "image/webp"]; export const ALLOWED_MIME_TYPES = ["image/jpeg", "image/png", "image/svg+xml", "image/webp"];
const createMarkdownParser = (showToc = true) => { const createMarkdownParser = (showToc = true) => {
@@ -61,7 +54,7 @@ const createMarkdownParser = (showToc = true) => {
const text = this.parser.parseInline(tokens); const text = this.parser.parseInline(tokens);
const raw = unescape(this.parser.parseInline(tokens, this.parser.textRenderer)) const raw = unescape(this.parser.parseInline(tokens, this.parser.textRenderer))
.trim() .trim()
.replace(/<[!\/a-z].*?>/gi, ""); .replace(/<[!a-z].*?>/gi, "");
const level = depth; const level = depth;
const id = `${prefix}${slugger.slug(raw.toLowerCase())}`; const id = `${prefix}${slugger.slug(raw.toLowerCase())}`;
const heading = { level, text, id, raw }; const heading = { level, text, id, raw };

View File

@@ -2,13 +2,29 @@
import { enhance } from "$app/forms"; import { enhance } from "$app/forms";
import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import type { ActionData } from "./$types"; import type { ActionData } from "./$types";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
const { form }: { form: ActionData } = $props(); const { form }: { form: ActionData } = $props();
let sending = $state(false);
</script> </script>
<SuccessOrError success={form?.success} message={form?.message} /> <SuccessOrError success={form?.success} message={form?.message} />
<form method="POST" use:enhance> {#if sending}
<LoadingSpinner />
{/if}
<form
method="POST"
use:enhance={() => {
sending = true;
return async ({ update }) => {
await update();
sending = false;
};
}}
>
<label> <label>
Username: Username:
<input type="text" name="username" required /> <input type="text" name="username" required />

View File

@@ -2,13 +2,29 @@
import { enhance } from "$app/forms"; import { enhance } from "$app/forms";
import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import type { ActionData } from "./$types"; import type { ActionData } from "./$types";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
const { form }: { form: ActionData } = $props(); const { form }: { form: ActionData } = $props();
let sending = $state(false);
</script> </script>
<SuccessOrError success={form?.success} message={form?.message} /> <SuccessOrError success={form?.success} message={form?.message} />
<form method="POST" use:enhance> {#if sending}
<LoadingSpinner />
{/if}
<form
method="POST"
use:enhance={() => {
sending = true;
return async ({ update }) => {
await update();
sending = false;
};
}}
>
<label> <label>
Username: Username:
<input type="text" name="username" minlength="3" maxlength="16" required /> <input type="text" name="username" minlength="3" maxlength="16" required />

View File

@@ -1,17 +1,23 @@
<script lang="ts"> <script lang="ts">
import { enhance } from "$app/forms"; import { enhance } from "$app/forms";
import DateTime from "$lib/components/DateTime.svelte"; import DateTime from "$lib/components/DateTime.svelte";
import { sortOptions } from "$lib/utils.js";
import { page } from "$app/stores"; import { page } from "$app/stores";
import Modal from "$lib/components/Modal.svelte"; import Modal from "$lib/components/Modal.svelte";
import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import type { ActionData, PageServerData } from "./$types"; import type { ActionData, PageServerData } from "./$types";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
const { form, data }: { form: ActionData; data: PageServerData } = $props(); const { form, data }: { form: ActionData; data: PageServerData } = $props();
let sending = $state(false);
</script> </script>
<SuccessOrError success={form?.success} message={form?.message} /> <SuccessOrError success={form?.success} message={form?.message} />
{#if sending}
<LoadingSpinner />
{/if}
<section id="create-website"> <section id="create-website">
<h2> <h2>
<a href="#create-website">Create website</a> <a href="#create-website">Create website</a>
@@ -24,9 +30,11 @@
method="POST" method="POST"
action="?/createWebsite" action="?/createWebsite"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update(); await update();
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >
@@ -112,9 +120,11 @@
method="POST" method="POST"
action="?/updateWebsite" action="?/updateWebsite"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update({ reset: false }); await update({ reset: false });
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >
@@ -145,9 +155,11 @@
method="POST" method="POST"
action="?/deleteWebsite" action="?/deleteWebsite"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update(); await update();
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >

View File

@@ -2,13 +2,20 @@
import { enhance } from "$app/forms"; import { enhance } from "$app/forms";
import Modal from "$lib/components/Modal.svelte"; import Modal from "$lib/components/Modal.svelte";
import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
import type { ActionData, PageServerData } from "./$types"; import type { ActionData, PageServerData } from "./$types";
const { data, form }: { data: PageServerData; form: ActionData } = $props(); const { data, form }: { data: PageServerData; form: ActionData } = $props();
let sending = $state(false);
</script> </script>
<SuccessOrError success={form?.success} message={form?.message} /> <SuccessOrError success={form?.success} message={form?.message} />
{#if sending}
<LoadingSpinner />
{/if}
<section id="overview"> <section id="overview">
<h2> <h2>
<a href="#overview">Overview</a> <a href="#overview">Overview</a>
@@ -31,7 +38,17 @@
<a href="#logout">Logout</a> <a href="#logout">Logout</a>
</h2> </h2>
<form method="POST" action="?/logout" use:enhance> <form
method="POST"
action="?/logout"
use:enhance={() => {
sending = true;
return async ({ update }) => {
await update();
sending = false;
};
}}
>
<button type="submit">Logout</button> <button type="submit">Logout</button>
</form> </form>
</section> </section>
@@ -53,9 +70,11 @@
method="POST" method="POST"
action="?/deleteAccount" action="?/deleteAccount"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update(); await update();
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >

View File

@@ -1,7 +1,7 @@
import type { Actions, PageServerLoad } from "./$types"; import type { Actions, PageServerLoad } from "./$types";
import { API_BASE_PREFIX } from "$lib/server/utils"; import { API_BASE_PREFIX } from "$lib/server/utils";
export const load: PageServerLoad = async ({ params, fetch, cookies, url }) => { export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
const globalSettingsData = await fetch( const globalSettingsData = await fetch(
`${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`, `${API_BASE_PREFIX}/settings?website_id=eq.${params.websiteId}`,
{ {
@@ -45,7 +45,7 @@ export const load: PageServerLoad = async ({ params, fetch, cookies, url }) => {
}; };
export const actions: Actions = { export const actions: Actions = {
updateGlobal: async ({ request, fetch, cookies, params, locals }) => { updateGlobal: async ({ request, fetch, cookies, params }) => {
const data = await request.formData(); const data = await request.formData();
const faviconFile = data.get("favicon") as File; const faviconFile = data.get("favicon") as File;

View File

@@ -5,6 +5,7 @@
import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import type { ActionData, LayoutServerData, PageServerData } from "./$types"; import type { ActionData, LayoutServerData, PageServerData } from "./$types";
import Modal from "$lib/components/Modal.svelte"; import Modal from "$lib/components/Modal.svelte";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
const { data, form }: { data: PageServerData & LayoutServerData; form: ActionData } = $props(); const { data, form }: { data: PageServerData & LayoutServerData; form: ActionData } = $props();
@@ -24,10 +25,16 @@
previewContent = newContent; previewContent = newContent;
} }
}; };
let sending = $state(false);
</script> </script>
<SuccessOrError success={form?.success} message={form?.message} /> <SuccessOrError success={form?.success} message={form?.message} />
{#if sending}
<LoadingSpinner />
{/if}
<WebsiteEditor <WebsiteEditor
id={data.website.id} id={data.website.id}
contentType={data.website.content_type} contentType={data.website.content_type}
@@ -44,8 +51,10 @@
method="POST" method="POST"
enctype="multipart/form-data" enctype="multipart/form-data"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update({ reset: false }); await update({ reset: false });
sending = false;
}; };
}} }}
> >
@@ -98,8 +107,10 @@
method="POST" method="POST"
enctype="multipart/form-data" enctype="multipart/form-data"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update({ reset: false }); await update({ reset: false });
sending = false;
}; };
}} }}
> >
@@ -148,8 +159,10 @@
action="?/updateHome" action="?/updateHome"
method="POST" method="POST"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update({ reset: false }); await update({ reset: false });
sending = false;
}; };
}} }}
> >
@@ -179,8 +192,10 @@
action="?/updateFooter" action="?/updateFooter"
method="POST" method="POST"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update({ reset: false }); await update({ reset: false });
sending = false;
}; };
}} }}
> >

View File

@@ -16,8 +16,6 @@ export const load: PageServerLoad = async ({ params, fetch, cookies, url, parent
baseFetchUrl += "&order=last_modified_at.desc,created_at.desc"; baseFetchUrl += "&order=last_modified_at.desc,created_at.desc";
} }
console.log(baseFetchUrl);
const parameters = new URLSearchParams(); const parameters = new URLSearchParams();
if (searchQuery) { if (searchQuery) {
@@ -67,7 +65,7 @@ export const load: PageServerLoad = async ({ params, fetch, cookies, url, parent
}; };
export const actions: Actions = { export const actions: Actions = {
createArticle: async ({ request, fetch, cookies, params, locals }) => { createArticle: async ({ request, fetch, cookies, params }) => {
const data = await request.formData(); const data = await request.formData();
const res = await fetch(`${API_BASE_PREFIX}/article`, { const res = await fetch(`${API_BASE_PREFIX}/article`, {

View File

@@ -1,17 +1,23 @@
<script lang="ts"> <script lang="ts">
import WebsiteEditor from "$lib/components/WebsiteEditor.svelte"; import WebsiteEditor from "$lib/components/WebsiteEditor.svelte";
import { sortOptions } from "$lib/utils.js";
import { page } from "$app/stores"; import { page } from "$app/stores";
import { enhance } from "$app/forms"; import { enhance } from "$app/forms";
import Modal from "$lib/components/Modal.svelte"; import Modal from "$lib/components/Modal.svelte";
import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
import type { ActionData, PageServerData } from "./$types"; import type { ActionData, PageServerData } from "./$types";
const { data, form }: { data: PageServerData; form: ActionData } = $props(); const { data, form }: { data: PageServerData; form: ActionData } = $props();
let sending = $state(false);
</script> </script>
<SuccessOrError success={form?.success} message={form?.message} /> <SuccessOrError success={form?.success} message={form?.message} />
{#if sending}
<LoadingSpinner />
{/if}
<WebsiteEditor <WebsiteEditor
id={data.website.id} id={data.website.id}
contentType={data.website.content_type} contentType={data.website.content_type}
@@ -30,9 +36,11 @@
method="POST" method="POST"
action="?/createArticle" action="?/createArticle"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update(); await update();
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >
@@ -127,9 +135,11 @@
method="POST" method="POST"
action="?/deleteArticle" action="?/deleteArticle"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update(); await update();
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >

View File

@@ -5,6 +5,7 @@
import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import type { ActionData, PageServerData } from "./$types"; import type { ActionData, PageServerData } from "./$types";
import Modal from "$lib/components/Modal.svelte"; import Modal from "$lib/components/Modal.svelte";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
import { handleImagePaste } from "$lib/utils"; import { handleImagePaste } from "$lib/utils";
const { data, form }: { data: PageServerData; form: ActionData } = $props(); const { data, form }: { data: PageServerData; form: ActionData } = $props();
@@ -24,10 +25,16 @@
previewContent = newContent; previewContent = newContent;
} }
}; };
let sending = $state(false);
</script> </script>
<SuccessOrError success={form?.success} message={form?.message} /> <SuccessOrError success={form?.success} message={form?.message} />
{#if sending}
<LoadingSpinner />
{/if}
<WebsiteEditor <WebsiteEditor
id={data.website.id} id={data.website.id}
contentType={data.website.content_type} contentType={data.website.content_type}
@@ -46,8 +53,10 @@
action="?/editArticle" action="?/editArticle"
enctype="multipart/form-data" enctype="multipart/form-data"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update({ reset: false }); await update({ reset: false });
sending = false;
}; };
}} }}
> >

View File

@@ -3,13 +3,20 @@
import WebsiteEditor from "$lib/components/WebsiteEditor.svelte"; import WebsiteEditor from "$lib/components/WebsiteEditor.svelte";
import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import Modal from "$lib/components/Modal.svelte"; import Modal from "$lib/components/Modal.svelte";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
import type { ActionData, PageServerData } from "./$types"; import type { ActionData, PageServerData } from "./$types";
const { data, form }: { data: PageServerData; form: ActionData } = $props(); const { data, form }: { data: PageServerData; form: ActionData } = $props();
let sending = $state(false);
</script> </script>
<SuccessOrError success={form?.success} message={form?.message} /> <SuccessOrError success={form?.success} message={form?.message} />
{#if sending}
<LoadingSpinner />
{/if}
<WebsiteEditor <WebsiteEditor
id={data.website.id} id={data.website.id}
contentType={data.website.content_type} contentType={data.website.content_type}
@@ -28,9 +35,11 @@
method="POST" method="POST"
action="?/createCategory" action="?/createCategory"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update(); await update();
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >
@@ -70,9 +79,11 @@
method="POST" method="POST"
action="?/updateCategory" action="?/updateCategory"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update({ reset: false }); await update({ reset: false });
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >
@@ -95,9 +106,11 @@
method="POST" method="POST"
action="?/deleteCategory" action="?/deleteCategory"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update(); await update();
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >

View File

@@ -3,13 +3,20 @@
import WebsiteEditor from "$lib/components/WebsiteEditor.svelte"; import WebsiteEditor from "$lib/components/WebsiteEditor.svelte";
import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import Modal from "$lib/components/Modal.svelte"; import Modal from "$lib/components/Modal.svelte";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
import type { ActionData, PageServerData } from "./$types"; import type { ActionData, PageServerData } from "./$types";
const { data, form }: { data: PageServerData; form: ActionData } = $props(); const { data, form }: { data: PageServerData; form: ActionData } = $props();
let sending = $state(false);
</script> </script>
<SuccessOrError success={form?.success} message={form?.message} /> <SuccessOrError success={form?.success} message={form?.message} />
{#if sending}
<LoadingSpinner />
{/if}
<WebsiteEditor <WebsiteEditor
id={data.website.id} id={data.website.id}
contentType={data.website.content_type} contentType={data.website.content_type}
@@ -28,9 +35,11 @@
method="POST" method="POST"
action="?/addCollaborator" action="?/addCollaborator"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update(); await update();
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >
@@ -74,9 +83,11 @@
method="POST" method="POST"
action="?/updateCollaborator" action="?/updateCollaborator"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update({ reset: false }); await update({ reset: false });
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >
@@ -103,9 +114,11 @@
method="POST" method="POST"
action="?/removeCollaborator" action="?/removeCollaborator"
use:enhance={() => { use:enhance={() => {
sending = true;
return async ({ update }) => { return async ({ update }) => {
await update(); await update();
window.location.hash = "!"; window.location.hash = "!";
sending = false;
}; };
}} }}
> >

View File

@@ -10,6 +10,30 @@ import DocsIndex from "$lib/templates/docs/DocsIndex.svelte";
import DocsArticle from "$lib/templates/docs/DocsArticle.svelte"; import DocsArticle from "$lib/templates/docs/DocsArticle.svelte";
import { dev } from "$app/environment"; import { dev } from "$app/environment";
interface WebsiteData {
id: string;
content_type: "Blog" | "Docs";
favicon_image: string | null;
title: string;
logo_type: "text" | "image";
logo_text: string | null;
logo_image: string | null;
main_content: string;
additional_text: string;
accent_color_light_theme: string;
accent_color_dark_theme: string;
articles: {
cover_image: string | null;
title: string;
publication_date: string;
meta_description: string;
main_content: string;
}[];
categorized_articles: {
[key: string]: { title: string; publication_date: string; meta_description: string }[];
};
}
export const load: PageServerLoad = async ({ params, fetch, cookies, parent }) => { export const load: PageServerLoad = async ({ params, fetch, cookies, parent }) => {
const websiteOverviewData = await fetch( const websiteOverviewData = await fetch(
`${API_BASE_PREFIX}/website_overview?id=eq.${params.websiteId}`, `${API_BASE_PREFIX}/website_overview?id=eq.${params.websiteId}`,
@@ -80,7 +104,7 @@ export const actions: Actions = {
} }
}; };
const generateStaticFiles = async (websiteData: any, isPreview: boolean = true) => { const generateStaticFiles = async (websiteData: WebsiteData, isPreview: boolean = true) => {
let head = ""; let head = "";
let body = ""; let body = "";
@@ -96,7 +120,7 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
logoType: websiteData.logo_type, logoType: websiteData.logo_type,
logo: logo:
websiteData.logo_type === "text" websiteData.logo_type === "text"
? websiteData.logo_text ? (websiteData.logo_text ?? "")
: `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`, : `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`,
mainContent: md(websiteData.main_content ?? "", false), mainContent: md(websiteData.main_content ?? "", false),
articles: websiteData.articles ?? [], articles: websiteData.articles ?? [],
@@ -116,7 +140,7 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
logoType: websiteData.logo_type, logoType: websiteData.logo_type,
logo: logo:
websiteData.logo_type === "text" websiteData.logo_type === "text"
? websiteData.logo_text ? (websiteData.logo_text ?? "")
: `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`, : `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`,
mainContent: md(websiteData.main_content ?? "", false), mainContent: md(websiteData.main_content ?? "", false),
categorizedArticles: websiteData.categorized_articles ?? [], categorizedArticles: websiteData.categorized_articles ?? [],
@@ -161,7 +185,7 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
logoType: websiteData.logo_type, logoType: websiteData.logo_type,
logo: logo:
websiteData.logo_type === "text" websiteData.logo_type === "text"
? websiteData.logo_text ? (websiteData.logo_text ?? "")
: `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`, : `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`,
coverImage: article.cover_image coverImage: article.cover_image
? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${article.cover_image}` ? `${API_BASE_PREFIX}/rpc/retrieve_file?id=${article.cover_image}`
@@ -184,7 +208,7 @@ const generateStaticFiles = async (websiteData: any, isPreview: boolean = true)
logoType: websiteData.logo_type, logoType: websiteData.logo_type,
logo: logo:
websiteData.logo_type === "text" websiteData.logo_type === "text"
? websiteData.logo_text ? (websiteData.logo_text ?? "")
: `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`, : `${API_BASE_PREFIX}/rpc/retrieve_file?id=${websiteData.logo_image}`,
mainContent: md(article.main_content ?? ""), mainContent: md(article.main_content ?? ""),
categorizedArticles: websiteData.categorized_articles ?? [], categorizedArticles: websiteData.categorized_articles ?? [],

View File

@@ -3,14 +3,21 @@
import WebsiteEditor from "$lib/components/WebsiteEditor.svelte"; import WebsiteEditor from "$lib/components/WebsiteEditor.svelte";
import SuccessOrError from "$lib/components/SuccessOrError.svelte"; import SuccessOrError from "$lib/components/SuccessOrError.svelte";
import type { ActionData, PageServerData } from "./$types"; import type { ActionData, PageServerData } from "./$types";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
const { data, form }: { data: PageServerData; form: ActionData } = $props(); const { data, form }: { data: PageServerData; form: ActionData } = $props();
const prodWebsiteUrl = data.websitePreviewUrl.replace("/previews", ""); const prodWebsiteUrl = data.websitePreviewUrl.replace("/previews", "");
let sending = $state(false);
</script> </script>
<SuccessOrError success={form?.success} message={form?.message} /> <SuccessOrError success={form?.success} message={form?.message} />
{#if sending}
<LoadingSpinner />
{/if}
<WebsiteEditor <WebsiteEditor
id={data.website.id} id={data.website.id}
contentType={data.website.content_type} contentType={data.website.content_type}
@@ -27,7 +34,17 @@
is published. If you are happy with the results, click the button below and your website will is published. If you are happy with the results, click the button below and your website will
be published on the Internet. be published on the Internet.
</p> </p>
<form method="POST" action="?/publishWebsite" use:enhance> <form
method="POST"
action="?/publishWebsite"
use:enhance={() => {
sending = true;
return async ({ update }) => {
await update();
sending = false;
};
}}
>
<button type="submit">Publish</button> <button type="submit">Publish</button>
</form> </form>

View File

@@ -3,9 +3,20 @@
import { page } from "$app/stores"; import { page } from "$app/stores";
import type { LayoutServerData } from "./$types"; import type { LayoutServerData } from "./$types";
import type { Snippet } from "svelte"; import type { Snippet } from "svelte";
import { beforeNavigate, afterNavigate } from "$app/navigation";
import LoadingSpinner from "$lib/components/LoadingSpinner.svelte";
const { data, children }: { data: LayoutServerData; children: Snippet } = $props(); const { data, children }: { data: LayoutServerData; children: Snippet } = $props();
let loading = $state(false);
beforeNavigate(() => {
loading = true;
});
afterNavigate(() => {
loading = false;
});
const isProjectRoute = $derived($page.url.pathname.startsWith("/website") && !$page.error); const isProjectRoute = $derived($page.url.pathname.startsWith("/website") && !$page.error);
const routeName = $derived( const routeName = $derived(
$page.url.pathname === "/" $page.url.pathname === "/"
@@ -14,6 +25,10 @@
); );
</script> </script>
{#if loading}
<LoadingSpinner />
{/if}
<svelte:head> <svelte:head>
<title>archtika | {routeName.replaceAll("/", " - ")}</title> <title>archtika | {routeName.replaceAll("/", " - ")}</title>
</svelte:head> </svelte:head>