mirror of
https://github.com/thiloho/archtika.git
synced 2025-11-22 10:51:36 +01:00
Add some base styles
This commit is contained in:
169
web-app/src/app.css
Normal file
169
web-app/src/app.css
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
:root {
|
||||||
|
--bg-primary: white;
|
||||||
|
--bg-secondary: hsl(0 0% 95%);
|
||||||
|
--bg-tertiary: hsl(0 0% 90%);
|
||||||
|
|
||||||
|
--color-text: black;
|
||||||
|
--color-text-invert: white;
|
||||||
|
--color-accent: hsl(210, 100%, 30%);
|
||||||
|
--color-success: hsl(105, 100%, 30%);
|
||||||
|
--color-error: hsl(0, 100%, 30%);
|
||||||
|
|
||||||
|
--border-primary: 0.0625rem solid var(--bg-tertiary);
|
||||||
|
--border-radius: 0.125rem;
|
||||||
|
|
||||||
|
color-scheme: light;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--bg-primary: hsl(0 0% 15%);
|
||||||
|
--bg-secondary: hsl(0 0% 20%);
|
||||||
|
--bg-tertiary: hsl(0 0% 25%);
|
||||||
|
|
||||||
|
--color-text: white;
|
||||||
|
--color-text-invert: black;
|
||||||
|
--color-accent: hsl(210, 100%, 80%);
|
||||||
|
--color-success: hsl(105, 100%, 80%);
|
||||||
|
--color-error: hsl(0, 100%, 80%);
|
||||||
|
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
line-height: 1.5;
|
||||||
|
font-family: system-ui, sans-serif;
|
||||||
|
background-color: var(--bg-primary);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-block-size: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
section + section {
|
||||||
|
margin-block-start: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
label,
|
||||||
|
select,
|
||||||
|
summary,
|
||||||
|
[role="button"],
|
||||||
|
[role="option"] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
button,
|
||||||
|
textarea,
|
||||||
|
select,
|
||||||
|
a[role="button"] {
|
||||||
|
font: inherit;
|
||||||
|
color: inherit;
|
||||||
|
border: var(--border-primary);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
padding-inline: 0.5rem;
|
||||||
|
padding-block: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
background-color: var(--bg-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
a[role="button"] {
|
||||||
|
display: inline-block;
|
||||||
|
max-inline-size: fit-content;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
a[role="button"] {
|
||||||
|
background-color: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
:is(button, a[role="button"]):hover {
|
||||||
|
background-color: var(--bg-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
img,
|
||||||
|
picture,
|
||||||
|
svg,
|
||||||
|
video {
|
||||||
|
max-inline-size: 100%;
|
||||||
|
block-size: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4 {
|
||||||
|
line-height: 1.2;
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
form[method="POST"] {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
form > button[type="submit"] {
|
||||||
|
max-inline-size: 30ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
form[method="GET"] {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 20ch), 1fr));
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
form[method="GET"] > button[type="submit"] {
|
||||||
|
align-self: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
form > label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.25rem;
|
||||||
|
max-inline-size: 30ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
form > label:has(textarea) {
|
||||||
|
max-inline-size: 65ch;
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="stylesheet" href="https://unpkg.com/@acab/reset.css" />
|
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
|||||||
52
web-app/src/lib/components/Modal.svelte
Normal file
52
web-app/src/lib/components/Modal.svelte
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
|
const { children, id, text } = $props<{ children: Snippet; id: string; text: string }>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<a href={`#${id}`} role="button">{text}</a>
|
||||||
|
|
||||||
|
<div {id} class="modal">
|
||||||
|
<div class="modal__content">
|
||||||
|
{@render children()}
|
||||||
|
<a href="#!" role="button">Close</a>
|
||||||
|
</div>
|
||||||
|
<a href="#!" class="modal__closeoverlay" aria-label="Close modal"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.modal {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal:target {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__closeoverlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 10;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2rem;
|
||||||
|
padding-inline: 1rem;
|
||||||
|
padding-block: 2rem;
|
||||||
|
background-color: var(--bg-primary);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
border: var(--border-primary);
|
||||||
|
z-index: 20;
|
||||||
|
position: absolute;
|
||||||
|
max-inline-size: 300px;
|
||||||
|
margin-inline: auto;
|
||||||
|
inset-block-start: 2rem;
|
||||||
|
inset-inline-start: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -12,10 +12,10 @@
|
|||||||
<div class="operations">
|
<div class="operations">
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
|
|
||||||
<div>
|
<nav class="operations__nav">
|
||||||
<a href="/website/{id}">Settings</a>
|
<a href="/website/{id}">Settings</a>
|
||||||
<a href="/website/{id}/articles">Articles</a>
|
<a href="/website/{id}/articles">Articles</a>
|
||||||
</div>
|
</nav>
|
||||||
|
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</div>
|
</div>
|
||||||
@@ -34,11 +34,22 @@
|
|||||||
|
|
||||||
.operations {
|
.operations {
|
||||||
inline-size: 50%;
|
inline-size: 50%;
|
||||||
border-inline-end: 0.0625rem solid hsl(0 0% 50%);
|
border-inline-end: var(--border-primary);
|
||||||
resize: horizontal;
|
resize: horizontal;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.operations__nav {
|
||||||
|
margin-block: 1rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.operations__nav > a {
|
||||||
|
display: inline-block;
|
||||||
|
padding-inline: 0.5rem;
|
||||||
|
padding-block: 0.25rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.preview {
|
.preview {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import DateTime from "$lib/components/DateTime.svelte";
|
import DateTime from "$lib/components/DateTime.svelte";
|
||||||
import { sortOptions } from "$lib/utils.js";
|
import { sortOptions } from "$lib/utils.js";
|
||||||
import { page } from "$app/stores";
|
import { page } from "$app/stores";
|
||||||
|
import Modal from "$lib/components/Modal.svelte";
|
||||||
|
|
||||||
const { form, data } = $props();
|
const { form, data } = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -18,21 +19,25 @@
|
|||||||
<section>
|
<section>
|
||||||
<h2>Create website</h2>
|
<h2>Create website</h2>
|
||||||
|
|
||||||
<form method="POST" action="?/createWebsite" use:enhance>
|
<Modal id="create-website" text="Create website">
|
||||||
<label>
|
<h3>Create website</h3>
|
||||||
Type:
|
|
||||||
<select name="content-type">
|
|
||||||
<option value="Blog">Blog</option>
|
|
||||||
<option value="Docs">Docs</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Title:
|
|
||||||
<input type="text" name="title" />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<button type="submit">Submit</button>
|
<form method="POST" action="?/createWebsite" use:enhance>
|
||||||
</form>
|
<label>
|
||||||
|
Type:
|
||||||
|
<select name="content-type">
|
||||||
|
<option value="Blog">Blog</option>
|
||||||
|
<option value="Docs">Docs</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Title:
|
||||||
|
<input type="text" name="title" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{#if data.totalWebsiteCount > 0}
|
{#if data.totalWebsiteCount > 0}
|
||||||
@@ -69,52 +74,85 @@
|
|||||||
<button type="submit">Submit</button>
|
<button type="submit">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{#each data.websites as { id, content_type, title, created_at }}
|
<div class="website-grid">
|
||||||
<article>
|
{#each data.websites as { id, content_type, title, created_at }}
|
||||||
<h3>
|
<article class="website-card">
|
||||||
<a href="/website/{id}">{title}</a>
|
<h3>
|
||||||
</h3>
|
<a href="/website/{id}">{title}</a>
|
||||||
<p>
|
</h3>
|
||||||
<strong>Type:</strong>
|
<ul>
|
||||||
{content_type}
|
<li>
|
||||||
</p>
|
<strong>Type:</strong>
|
||||||
<p>
|
{content_type}
|
||||||
<strong>Created at:</strong>
|
</li>
|
||||||
<DateTime date={created_at} />
|
<li>
|
||||||
</p>
|
<strong>Created at:</strong>
|
||||||
<details>
|
<DateTime date={created_at} />
|
||||||
<summary>Update</summary>
|
</li>
|
||||||
<form
|
</ul>
|
||||||
method="POST"
|
<div class="website-card__actions">
|
||||||
action="?/updateWebsite"
|
<Modal id="update-website-{id}" text="Update">
|
||||||
use:enhance={() => {
|
<h4>Update website</h4>
|
||||||
return async ({ update }) => {
|
|
||||||
await update({ reset: false });
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<input type="hidden" name="id" value={id} />
|
|
||||||
<label>
|
|
||||||
Title
|
|
||||||
<input type="text" name="title" value={title} />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<button type="submit">Submit</button>
|
<form
|
||||||
</form>
|
method="POST"
|
||||||
</details>
|
action="?/updateWebsite"
|
||||||
<details>
|
use:enhance={() => {
|
||||||
<summary>Delete</summary>
|
return async ({ update }) => {
|
||||||
<p>
|
await update({ reset: false });
|
||||||
<strong>Caution!</strong>
|
};
|
||||||
Deleting this website will irretrievably erase all data.
|
}}
|
||||||
</p>
|
>
|
||||||
<form method="POST" action="?/deleteWebsite" use:enhance>
|
<input type="hidden" name="id" value={id} />
|
||||||
<input type="hidden" name="id" value={id} />
|
<label>
|
||||||
|
Title
|
||||||
|
<input type="text" name="title" value={title} />
|
||||||
|
</label>
|
||||||
|
|
||||||
<button type="submit">Permanently delete website</button>
|
<button type="submit">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
</details>
|
</Modal>
|
||||||
</article>
|
<Modal id="delete-website-{id}" text="Delete">
|
||||||
{/each}
|
<h4>Delete website</h4>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Caution!</strong>
|
||||||
|
Deleting this website will irretrievably erase all data.
|
||||||
|
</p>
|
||||||
|
<form method="POST" action="?/deleteWebsite" use:enhance>
|
||||||
|
<input type="hidden" name="id" value={id} />
|
||||||
|
|
||||||
|
<button type="submit">Permanently delete website</button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.website-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 1rem;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 35ch), 1fr));
|
||||||
|
margin-block-start: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-card {
|
||||||
|
border: var(--border-primary);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
padding-inline: 1rem;
|
||||||
|
padding-block: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.website-card__actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { enhance } from "$app/forms";
|
import { enhance } from "$app/forms";
|
||||||
|
import Modal from "$lib/components/Modal.svelte";
|
||||||
|
|
||||||
const { data, form } = $props();
|
const { data, form } = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -36,12 +37,21 @@
|
|||||||
<section>
|
<section>
|
||||||
<h2>Delete account</h2>
|
<h2>Delete account</h2>
|
||||||
|
|
||||||
<form method="POST" action="?/deleteAccount" use:enhance>
|
<Modal id="delete-account" text="Delete account">
|
||||||
<label>
|
<h3>Delete account</h3>
|
||||||
Password:
|
|
||||||
<input type="password" name="password" required />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<button type="submit">Delete account</button>
|
<p>
|
||||||
</form>
|
<strong>Caution!</strong>
|
||||||
|
Deleting your account will irretrievably erase all data.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="POST" action="?/deleteAccount" use:enhance>
|
||||||
|
<label>
|
||||||
|
Password:
|
||||||
|
<input type="password" name="password" required />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button type="submit">Permanently delete account</button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -20,114 +20,114 @@
|
|||||||
previewContent={data.home.main_content}
|
previewContent={data.home.main_content}
|
||||||
>
|
>
|
||||||
<section>
|
<section>
|
||||||
<h2>Settings</h2>
|
<h2>Global</h2>
|
||||||
|
<form
|
||||||
|
action="?/updateGlobal"
|
||||||
|
method="POST"
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
use:enhance={() => {
|
||||||
|
return async ({ update }) => {
|
||||||
|
await update({ reset: false });
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<label>
|
||||||
|
Light accent color:
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
name="accent-color-light"
|
||||||
|
value={data.globalSettings.accent_color_light_theme}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Light accent color:
|
||||||
|
<input
|
||||||
|
type="color"
|
||||||
|
name="accent-color-dark"
|
||||||
|
value={data.globalSettings.accent_color_dark_theme}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Favicon:
|
||||||
|
<input type="file" name="favicon" accept={ALLOWED_MIME_TYPES.join(", ")} />
|
||||||
|
</label>
|
||||||
|
|
||||||
<section>
|
<button type="submit">Submit</button>
|
||||||
<h3>Global</h3>
|
</form>
|
||||||
<form
|
</section>
|
||||||
action="?/updateGlobal"
|
|
||||||
method="POST"
|
|
||||||
enctype="multipart/form-data"
|
|
||||||
use:enhance={() => {
|
|
||||||
return async ({ update }) => {
|
|
||||||
await update({ reset: false });
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
Light accent color:
|
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
name="accent-color-light"
|
|
||||||
value={data.globalSettings.accent_color_light_theme}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Light accent color:
|
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
name="accent-color-dark"
|
|
||||||
value={data.globalSettings.accent_color_dark_theme}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Favicon:
|
|
||||||
<input type="file" name="favicon" accept={ALLOWED_MIME_TYPES.join(", ")} />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<button type="submit">Submit</button>
|
<section>
|
||||||
</form>
|
<h2>Header</h2>
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
<form
|
||||||
<h3>Header</h3>
|
action="?/updateHeader"
|
||||||
|
method="POST"
|
||||||
|
enctype="multipart/form-data"
|
||||||
|
use:enhance={() => {
|
||||||
|
return async ({ update }) => {
|
||||||
|
await update({ reset: false });
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<label>
|
||||||
|
Logo type:
|
||||||
|
<select name="logo-type">
|
||||||
|
<option value="text" selected={"text" === data.header.logo_type}>Text</option>
|
||||||
|
<option value="image" selected={"image" === data.header.logo_type}>Image</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Logo text:
|
||||||
|
<input type="text" name="logo-text" value={data.header.logo_text} />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Logo image:
|
||||||
|
<input type="file" name="logo-image" accept={ALLOWED_MIME_TYPES.join(", ")} />
|
||||||
|
</label>
|
||||||
|
|
||||||
<form
|
<button type="submit">Submit</button>
|
||||||
action="?/updateHeader"
|
</form>
|
||||||
method="POST"
|
</section>
|
||||||
enctype="multipart/form-data"
|
|
||||||
use:enhance={() => {
|
|
||||||
return async ({ update }) => {
|
|
||||||
await update({ reset: false });
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
Logo type:
|
|
||||||
<select name="logo-type">
|
|
||||||
<option value="text" selected={"text" === data.header.logo_type}>Text</option>
|
|
||||||
<option value="image" selected={"image" === data.header.logo_type}>Image</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Logo text:
|
|
||||||
<input type="text" name="logo-text" value={data.header.logo_text} />
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Logo image:
|
|
||||||
<input type="file" name="logo-image" accept={ALLOWED_MIME_TYPES.join(", ")} />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<button type="submit">Submit</button>
|
<section>
|
||||||
</form>
|
<h2>Home</h2>
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
<form
|
||||||
<h3>Home</h3>
|
action="?/updateHome"
|
||||||
|
method="POST"
|
||||||
|
use:enhance={() => {
|
||||||
|
return async ({ update }) => {
|
||||||
|
await update({ reset: false });
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<label>
|
||||||
|
Main content:
|
||||||
|
<textarea name="main-content">{data.home.main_content}</textarea>
|
||||||
|
</label>
|
||||||
|
|
||||||
<form
|
<button type="submit">Submit</button>
|
||||||
action="?/updateHome"
|
</form>
|
||||||
method="POST"
|
</section>
|
||||||
use:enhance={() => {
|
|
||||||
return async ({ update }) => {
|
|
||||||
await update({ reset: false });
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<label>
|
|
||||||
Main content:
|
|
||||||
<textarea name="main-content">{data.home.main_content}</textarea>
|
|
||||||
</label>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h3>Footer</h3>
|
<h2>Footer</h2>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
action="?/updateFooter"
|
action="?/updateFooter"
|
||||||
method="POST"
|
method="POST"
|
||||||
use:enhance={() => {
|
use:enhance={() => {
|
||||||
return async ({ update }) => {
|
return async ({ update }) => {
|
||||||
await update({ reset: false });
|
await update({ reset: false });
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<label>
|
<label>
|
||||||
Additional text:
|
Additional text:
|
||||||
<textarea name="additional-text">{data.footer.additional_text}</textarea>
|
<textarea name="additional-text">{data.footer.additional_text}</textarea>
|
||||||
</label>
|
</label>
|
||||||
</form>
|
|
||||||
</section>
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
</section>
|
</section>
|
||||||
</WebsiteEditor>
|
</WebsiteEditor>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { sortOptions } from "$lib/utils.js";
|
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";
|
||||||
|
|
||||||
const { data, form } = $props();
|
const { data, form } = $props();
|
||||||
</script>
|
</script>
|
||||||
@@ -23,14 +24,18 @@
|
|||||||
<section>
|
<section>
|
||||||
<h2>Create article</h2>
|
<h2>Create article</h2>
|
||||||
|
|
||||||
<form method="POST" action="?/createArticle" use:enhance>
|
<Modal id="create-article" text="Create article">
|
||||||
<label>
|
<h3>Create article</h3>
|
||||||
Title:
|
|
||||||
<input type="text" name="title" />
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<button type="submit">Submit</button>
|
<form method="POST" action="?/createArticle" use:enhance>
|
||||||
</form>
|
<label>
|
||||||
|
Title:
|
||||||
|
<input type="text" name="title" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<button type="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{#if data.totalArticleCount > 0}
|
{#if data.totalArticleCount > 0}
|
||||||
@@ -60,23 +65,49 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
{#each data.articles as { id, title }}
|
{#each data.articles as { id, title }}
|
||||||
<article>
|
<article class="article-card">
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
<a href="/website/{data.website.id}/articles/{id}">Edit</a>
|
|
||||||
<details>
|
|
||||||
<summary>Delete</summary>
|
|
||||||
<p>
|
|
||||||
<strong>Caution!</strong>
|
|
||||||
Deleting this article will irretrievably erase all data.
|
|
||||||
</p>
|
|
||||||
<form method="POST" action="?/deleteArticle" use:enhance>
|
|
||||||
<input type="hidden" name="id" value={id} />
|
|
||||||
|
|
||||||
<button type="submit">Permanently delete article</button>
|
<div class="article-card__actions">
|
||||||
</form>
|
<a href="/website/{data.website.id}/articles/{id}">Edit</a>
|
||||||
</details>
|
<Modal id="delete-article-{id}" text="Delete">
|
||||||
|
<h4>Delete article</h4>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<strong>Caution!</strong>
|
||||||
|
Deleting this article will irretrievably erase all data.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="POST" action="?/deleteArticle" use:enhance>
|
||||||
|
<input type="hidden" name="id" value={id} />
|
||||||
|
|
||||||
|
<button type="submit">Permanently delete article</button>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
</article>
|
</article>
|
||||||
{/each}
|
{/each}
|
||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
</WebsiteEditor>
|
</WebsiteEditor>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.article-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 2rem;
|
||||||
|
row-gap: 0.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-card:nth-of-type(1) {
|
||||||
|
margin-block-start: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-card__actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import "../app.css";
|
||||||
import { page } from "$app/stores";
|
import { page } from "$app/stores";
|
||||||
const { data, children } = $props();
|
const { data, children } = $props();
|
||||||
|
|
||||||
const isProjectRoute = $derived($page.url.pathname.startsWith("/website"));
|
const isProjectRoute = $derived($page.url.pathname.startsWith("/website"));
|
||||||
|
const routeName = $derived(
|
||||||
|
$page.url.pathname === "/"
|
||||||
|
? "Dashboard"
|
||||||
|
: `${$page.url.pathname.charAt(1).toUpperCase()}${$page.url.pathname.slice(2)}`
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<nav>
|
<nav>
|
||||||
@@ -18,7 +24,7 @@
|
|||||||
|
|
||||||
{#if !isProjectRoute}
|
{#if !isProjectRoute}
|
||||||
<header>
|
<header>
|
||||||
<h1>{$page.url.pathname}</h1>
|
<h1>{routeName}</h1>
|
||||||
</header>
|
</header>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
@@ -42,28 +48,28 @@
|
|||||||
margin-inline: auto;
|
margin-inline: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav > *:first-child {
|
||||||
|
margin-inline-end: auto;
|
||||||
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
margin-block-start: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
inline-size: min(100% - 2rem, 1536px);
|
inline-size: min(100% - 2rem, 1536px);
|
||||||
block-size: calc(100vh - 7rem);
|
block-size: calc(100vh - 7rem);
|
||||||
border: 0.0625rem solid hsl(0 0% 50%);
|
border: var(--border-primary);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
padding-block: 0;
|
padding-block: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(section) {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
:global(form) {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user