diff --git a/.sqlx/query-990fae3e6568e19f18784fb562b0f712f5bd2373a5be11cad1714daf357c0f34.json b/.sqlx/query-170d08a3b1effa554fe831e43a8d7b640fbddaa348289360fb520057c0ef3272.json similarity index 77% rename from .sqlx/query-990fae3e6568e19f18784fb562b0f712f5bd2373a5be11cad1714daf357c0f34.json rename to .sqlx/query-170d08a3b1effa554fe831e43a8d7b640fbddaa348289360fb520057c0ef3272.json index 8c996a5..44302f6 100644 --- a/.sqlx/query-990fae3e6568e19f18784fb562b0f712f5bd2373a5be11cad1714daf357c0f34.json +++ b/.sqlx/query-170d08a3b1effa554fe831e43a8d7b640fbddaa348289360fb520057c0ef3272.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n id, \n slug, \n name,\n short_description,\n description, \n status as \"status: ProjectStatus\", \n github_repo, \n demo_url, \n\n last_github_activity, \n created_at, \n updated_at\n FROM projects\n WHERE id = $1\n ", + "query": "\n SELECT\n id,\n slug,\n name,\n short_description,\n description,\n status as \"status: ProjectStatus\",\n github_repo,\n demo_url,\n\n last_github_activity,\n created_at,\n updated_at\n FROM projects\n WHERE id = $1\n ", "describe": { "columns": [ { @@ -90,5 +90,5 @@ false ] }, - "hash": "990fae3e6568e19f18784fb562b0f712f5bd2373a5be11cad1714daf357c0f34" + "hash": "170d08a3b1effa554fe831e43a8d7b640fbddaa348289360fb520057c0ef3272" } diff --git a/.sqlx/query-1dce5dfc93dcb882e6bdeacbe4156ad56c0e14144b2e0a6d8b1fcc29196dcca0.json b/.sqlx/query-2bf2a98d854d1c467f31edc4b73fa0e6cc8610aec8e07f3de50d76b4900bef0b.json similarity index 70% rename from .sqlx/query-1dce5dfc93dcb882e6bdeacbe4156ad56c0e14144b2e0a6d8b1fcc29196dcca0.json rename to .sqlx/query-2bf2a98d854d1c467f31edc4b73fa0e6cc8610aec8e07f3de50d76b4900bef0b.json index 29661cb..224c197 100644 --- a/.sqlx/query-1dce5dfc93dcb882e6bdeacbe4156ad56c0e14144b2e0a6d8b1fcc29196dcca0.json +++ b/.sqlx/query-2bf2a98d854d1c467f31edc4b73fa0e6cc8610aec8e07f3de50d76b4900bef0b.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n status as \"status!: ProjectStatus\",\n COUNT(*)::int as \"count!\"\n FROM projects\n GROUP BY status\n ", + "query": "\n SELECT\n status as \"status!: ProjectStatus\",\n COUNT(*)::int as \"count!\"\n FROM projects\n GROUP BY status\n ", "describe": { "columns": [ { @@ -34,5 +34,5 @@ null ] }, - "hash": "1dce5dfc93dcb882e6bdeacbe4156ad56c0e14144b2e0a6d8b1fcc29196dcca0" + "hash": "2bf2a98d854d1c467f31edc4b73fa0e6cc8610aec8e07f3de50d76b4900bef0b" } diff --git a/.sqlx/query-ad86ca2613973d89e49b3bea1b3f0c8cf0885e431aea0cd349998de717c5e776.json b/.sqlx/query-538842bf07a2ac3fca612413e5a8e10769b2fc4d90d463040911f416314a42ac.json similarity index 92% rename from .sqlx/query-ad86ca2613973d89e49b3bea1b3f0c8cf0885e431aea0cd349998de717c5e776.json rename to .sqlx/query-538842bf07a2ac3fca612413e5a8e10769b2fc4d90d463040911f416314a42ac.json index b9e410c..76e9fa5 100644 --- a/.sqlx/query-ad86ca2613973d89e49b3bea1b3f0c8cf0885e431aea0cd349998de717c5e776.json +++ b/.sqlx/query-538842bf07a2ac3fca612413e5a8e10769b2fc4d90d463040911f416314a42ac.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n INSERT INTO projects (slug, name, short_description, description, status, github_repo, demo_url)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n RETURNING id, slug, name, short_description, description, status as \"status: ProjectStatus\", \n github_repo, demo_url, last_github_activity, created_at, updated_at\n ", + "query": "\n INSERT INTO projects (slug, name, short_description, description, status, github_repo, demo_url)\n VALUES ($1, $2, $3, $4, $5, $6, $7)\n RETURNING id, slug, name, short_description, description, status as \"status: ProjectStatus\",\n github_repo, demo_url, last_github_activity, created_at, updated_at\n ", "describe": { "columns": [ { @@ -108,5 +108,5 @@ false ] }, - "hash": "ad86ca2613973d89e49b3bea1b3f0c8cf0885e431aea0cd349998de717c5e776" + "hash": "538842bf07a2ac3fca612413e5a8e10769b2fc4d90d463040911f416314a42ac" } diff --git a/.sqlx/query-5514e52a1311c6e10f84d60f906c90178894997da3b13be0323c6172ecb419db.json b/.sqlx/query-7210d993016792490832230c09ed3c4dfc1a6292fcc9c16ded0b182b9952b7ce.json similarity index 77% rename from .sqlx/query-5514e52a1311c6e10f84d60f906c90178894997da3b13be0323c6172ecb419db.json rename to .sqlx/query-7210d993016792490832230c09ed3c4dfc1a6292fcc9c16ded0b182b9952b7ce.json index c3702c4..4a5661d 100644 --- a/.sqlx/query-5514e52a1311c6e10f84d60f906c90178894997da3b13be0323c6172ecb419db.json +++ b/.sqlx/query-7210d993016792490832230c09ed3c4dfc1a6292fcc9c16ded0b182b9952b7ce.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n id, \n slug, \n name,\n short_description,\n description, \n status as \"status: ProjectStatus\", \n github_repo, \n demo_url, \n last_github_activity, \n created_at, \n updated_at\n FROM projects\n ORDER BY updated_at DESC\n ", + "query": "\n SELECT\n id,\n slug,\n name,\n short_description,\n description,\n status as \"status: ProjectStatus\",\n github_repo,\n demo_url,\n last_github_activity,\n created_at,\n updated_at\n FROM projects\n ORDER BY updated_at DESC\n ", "describe": { "columns": [ { @@ -88,5 +88,5 @@ false ] }, - "hash": "5514e52a1311c6e10f84d60f906c90178894997da3b13be0323c6172ecb419db" + "hash": "7210d993016792490832230c09ed3c4dfc1a6292fcc9c16ded0b182b9952b7ce" } diff --git a/.sqlx/query-42723e40f3adff9eb24a701347c5b9c331ac7914b527ca6a98865fe4fb8a793c.json b/.sqlx/query-73404037b3f04ace5f775906ed25d4c572647889dc0185aed652038447ef9642.json similarity index 77% rename from .sqlx/query-42723e40f3adff9eb24a701347c5b9c331ac7914b527ca6a98865fe4fb8a793c.json rename to .sqlx/query-73404037b3f04ace5f775906ed25d4c572647889dc0185aed652038447ef9642.json index 621b5cf..3b46a08 100644 --- a/.sqlx/query-42723e40f3adff9eb24a701347c5b9c331ac7914b527ca6a98865fe4fb8a793c.json +++ b/.sqlx/query-73404037b3f04ace5f775906ed25d4c572647889dc0185aed652038447ef9642.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n p.id, \n p.slug, \n p.name,\n p.short_description,\n p.description, \n p.status as \"status: ProjectStatus\", \n p.github_repo, \n p.demo_url, \n p.last_github_activity, \n p.created_at, \n p.updated_at\n FROM projects p\n JOIN project_tags pt ON p.id = pt.project_id\n WHERE pt.tag_id = $1\n ORDER BY p.updated_at DESC\n ", + "query": "\n SELECT \n p.id, \n p.slug, \n p.name,\n p.short_description,\n p.description, \n p.status as \"status: super::ProjectStatus\", \n p.github_repo, \n p.demo_url, \n p.last_github_activity, \n p.created_at, \n p.updated_at\n FROM projects p\n JOIN project_tags pt ON p.id = pt.project_id\n WHERE pt.tag_id = $1\n ORDER BY p.updated_at DESC\n ", "describe": { "columns": [ { @@ -30,7 +30,7 @@ }, { "ordinal": 5, - "name": "status: ProjectStatus", + "name": "status: super::ProjectStatus", "type_info": { "Custom": { "name": "project_status", @@ -90,5 +90,5 @@ false ] }, - "hash": "42723e40f3adff9eb24a701347c5b9c331ac7914b527ca6a98865fe4fb8a793c" + "hash": "73404037b3f04ace5f775906ed25d4c572647889dc0185aed652038447ef9642" } diff --git a/.sqlx/query-960a24b5174e421d57f21944632a8356f6ef20fa5aae9c6126e4944dffbd5ee0.json b/.sqlx/query-92641a97d3f5329df38e2ecb456b1f43392b34bba0b286ab9bd75a8207354fea.json similarity index 76% rename from .sqlx/query-960a24b5174e421d57f21944632a8356f6ef20fa5aae9c6126e4944dffbd5ee0.json rename to .sqlx/query-92641a97d3f5329df38e2ecb456b1f43392b34bba0b286ab9bd75a8207354fea.json index da78df2..39a812f 100644 --- a/.sqlx/query-960a24b5174e421d57f21944632a8356f6ef20fa5aae9c6126e4944dffbd5ee0.json +++ b/.sqlx/query-92641a97d3f5329df38e2ecb456b1f43392b34bba0b286ab9bd75a8207354fea.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n id, \n slug, \n name,\n short_description,\n description, \n status as \"status: ProjectStatus\", \n github_repo, \n demo_url, \n last_github_activity, \n created_at, \n updated_at\n FROM projects\n WHERE status != 'hidden'\n ORDER BY updated_at DESC\n ", + "query": "\n SELECT\n id,\n slug,\n name,\n short_description,\n description,\n status as \"status: ProjectStatus\",\n github_repo,\n demo_url,\n last_github_activity,\n created_at,\n updated_at\n FROM projects\n WHERE status != 'hidden'\n ORDER BY updated_at DESC\n ", "describe": { "columns": [ { @@ -88,5 +88,5 @@ false ] }, - "hash": "960a24b5174e421d57f21944632a8356f6ef20fa5aae9c6126e4944dffbd5ee0" + "hash": "92641a97d3f5329df38e2ecb456b1f43392b34bba0b286ab9bd75a8207354fea" } diff --git a/.sqlx/query-e3c4842a1151f51f3871212c3e591103d04a7ff27b27d7d197fbd53593c06b74.json b/.sqlx/query-f97c44ba8b156a2f97cdbc240d1c760ae1efc041d1acd5c3462b96a9236dd3dc.json similarity index 77% rename from .sqlx/query-e3c4842a1151f51f3871212c3e591103d04a7ff27b27d7d197fbd53593c06b74.json rename to .sqlx/query-f97c44ba8b156a2f97cdbc240d1c760ae1efc041d1acd5c3462b96a9236dd3dc.json index 3bc4a87..9c2de97 100644 --- a/.sqlx/query-e3c4842a1151f51f3871212c3e591103d04a7ff27b27d7d197fbd53593c06b74.json +++ b/.sqlx/query-f97c44ba8b156a2f97cdbc240d1c760ae1efc041d1acd5c3462b96a9236dd3dc.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT \n id, \n slug, \n name,\n short_description,\n description, \n status as \"status: ProjectStatus\", \n github_repo, \n demo_url, \n\n last_github_activity, \n created_at, \n updated_at\n FROM projects\n WHERE slug = $1\n ", + "query": "\n SELECT\n id,\n slug,\n name,\n short_description,\n description,\n status as \"status: ProjectStatus\",\n github_repo,\n demo_url,\n\n last_github_activity,\n created_at,\n updated_at\n FROM projects\n WHERE slug = $1\n ", "describe": { "columns": [ { @@ -90,5 +90,5 @@ false ] }, - "hash": "e3c4842a1151f51f3871212c3e591103d04a7ff27b27d7d197fbd53593c06b74" + "hash": "f97c44ba8b156a2f97cdbc240d1c760ae1efc041d1acd5c3462b96a9236dd3dc" } diff --git a/.sqlx/query-39f5640a8e812c05d62a0c5ab696a60c70a942d8d29af1439c30a5b04ad4fc6b.json b/.sqlx/query-fdd7f3743ad2e6d1d571ee2d3dbd8b4d49074ef2cbe74dc79c6f4f638bdd3a88.json similarity index 83% rename from .sqlx/query-39f5640a8e812c05d62a0c5ab696a60c70a942d8d29af1439c30a5b04ad4fc6b.json rename to .sqlx/query-fdd7f3743ad2e6d1d571ee2d3dbd8b4d49074ef2cbe74dc79c6f4f638bdd3a88.json index 7df7b58..76d1664 100644 --- a/.sqlx/query-39f5640a8e812c05d62a0c5ab696a60c70a942d8d29af1439c30a5b04ad4fc6b.json +++ b/.sqlx/query-fdd7f3743ad2e6d1d571ee2d3dbd8b4d49074ef2cbe74dc79c6f4f638bdd3a88.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n UPDATE projects\n SET slug = $2, name = $3, short_description = $4, description = $5, \n status = $6, github_repo = $7, demo_url = $8\n WHERE id = $1\n RETURNING id, slug, name, short_description, description, status as \"status: ProjectStatus\", \n github_repo, demo_url, last_github_activity, created_at, updated_at\n ", + "query": "\n UPDATE projects\n SET slug = $2, name = $3, short_description = $4, description = $5,\n status = $6, github_repo = $7, demo_url = $8\n WHERE id = $1\n RETURNING id, slug, name, short_description, description, status as \"status: ProjectStatus\",\n github_repo, demo_url, last_github_activity, created_at, updated_at\n ", "describe": { "columns": [ { @@ -109,5 +109,5 @@ false ] }, - "hash": "39f5640a8e812c05d62a0c5ab696a60c70a942d8d29af1439c30a5b04ad4fc6b" + "hash": "fdd7f3743ad2e6d1d571ee2d3dbd8b4d49074ef2cbe74dc79c6f4f638bdd3a88" } diff --git a/Dockerfile b/Dockerfile index b2ab0ad..528103a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -63,7 +63,8 @@ COPY migrations/ ./migrations/ COPY --from=frontend /build/build/client ./web/build/client COPY --from=frontend /build/build/prerendered ./web/build/prerendered -# Build with real assets +# Build with real assets (use sqlx offline mode) +ENV SQLX_OFFLINE=true RUN cargo build --release && \ strip target/release/api @@ -83,6 +84,11 @@ COPY --from=frontend /build/build/client ./web/build/client COPY --from=frontend /build/build/*.js ./web/build/ COPY web/console-logger.js ./web/ +# Install production dependencies for SSR runtime +COPY web/package.json web/bun.lock ./web/ +RUN cd web && bun install --frozen-lockfile --production && \ + ln -s /app/web/node_modules /app/web/build/node_modules + # Create inline entrypoint script RUN cat > /entrypoint.sh << 'EOF' #!/bin/sh diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index a7a5d8e..8930df6 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -19,6 +19,7 @@ pub use tags::*; pub struct CreateTagRequest { pub name: String, pub slug: Option, + pub icon: Option, pub color: Option, } @@ -26,6 +27,7 @@ pub struct CreateTagRequest { pub struct UpdateTagRequest { pub name: String, pub slug: Option, + pub icon: Option, pub color: Option, } diff --git a/src/handlers/tags.rs b/src/handlers/tags.rs index 56a0ed3..70e89b6 100644 --- a/src/handlers/tags.rs +++ b/src/handlers/tags.rs @@ -73,7 +73,7 @@ pub async fn create_tag_handler( &state.pool, &payload.name, payload.slug.as_deref(), - None, // icon - not yet supported in admin UI + payload.icon.as_deref(), payload.color.as_deref(), ) .await @@ -214,7 +214,7 @@ pub async fn update_tag_handler( tag.id, &payload.name, payload.slug.as_deref(), - None, // icon - not yet supported in admin UI + payload.icon.as_deref(), payload.color.as_deref(), ) .await diff --git a/web/bun.lock b/web/bun.lock index b9972e0..50c87d6 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -8,6 +8,9 @@ "@fontsource-variable/schibsted-grotesk": "^5.2.8", "@fontsource/hanken-grotesk": "^5.1.0", "@fontsource/schibsted-grotesk": "^5.2.8", + "@iconify/json": "^2.2.425", + "@iconify/types": "^2.0.0", + "@iconify/utils": "^3.1.0", "@logtape/logtape": "^1.3.5", "@resvg/resvg-js": "^2.6.2", "@xevion/satori-html": "^0.4.1", @@ -20,7 +23,6 @@ "devDependencies": { "@eslint/js": "^9.39.2", "@fontsource/inter": "^5.2.8", - "@iconify/json": "^2.2.424", "@sveltejs/kit": "^2.21.0", "@sveltejs/vite-plugin-svelte": "^6.2.1", "@tailwindcss/vite": "^4.1.11", @@ -140,7 +142,7 @@ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="], - "@iconify/json": ["@iconify/json@2.2.424", "", { "dependencies": { "@iconify/types": "*", "pathe": "^2.0.3" } }, "sha512-Lxx8ad2DgyTGV88ec0mlaJ+OSjr0SyU0j8Awfbxl9JrxxHmBEFQJ+jywhztWAhLnaMUG3+G1htNJYzEsoAsNMQ=="], + "@iconify/json": ["@iconify/json@2.2.425", "", { "dependencies": { "@iconify/types": "*", "pathe": "^2.0.3" } }, "sha512-RJcJeoLFAmKPr7e7bP7gw33ASBSONuFsiCg35cEvrf/s8DDG5+C9eSqOvIiggNwZwwjYeGNKIJ7RduJTWgN0IQ=="], "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], diff --git a/web/package.json b/web/package.json index 8ae08e1..8211c10 100644 --- a/web/package.json +++ b/web/package.json @@ -20,6 +20,9 @@ "@fontsource-variable/schibsted-grotesk": "^5.2.8", "@fontsource/hanken-grotesk": "^5.1.0", "@fontsource/schibsted-grotesk": "^5.2.8", + "@iconify/json": "^2.2.425", + "@iconify/types": "^2.0.0", + "@iconify/utils": "^3.1.0", "@logtape/logtape": "^1.3.5", "@resvg/resvg-js": "^2.6.2", "@xevion/satori-html": "^0.4.1", @@ -32,7 +35,6 @@ "devDependencies": { "@eslint/js": "^9.39.2", "@fontsource/inter": "^5.2.8", - "@iconify/json": "^2.2.424", "@sveltejs/kit": "^2.21.0", "@sveltejs/vite-plugin-svelte": "^6.2.1", "@tailwindcss/vite": "^4.1.11", diff --git a/web/src/lib/admin-types.ts b/web/src/lib/admin-types.ts index 7155c07..9901366 100644 --- a/web/src/lib/admin-types.ts +++ b/web/src/lib/admin-types.ts @@ -49,6 +49,7 @@ export interface UpdateProjectData extends CreateProjectData { export interface CreateTagData { name: string; slug?: string; + icon?: string; color?: string; } diff --git a/web/src/lib/api.server.ts b/web/src/lib/api.server.ts index be61f4f..00d962c 100644 --- a/web/src/lib/api.server.ts +++ b/web/src/lib/api.server.ts @@ -15,13 +15,7 @@ interface BunFetchOptions extends RequestInit { * Create a socket-aware fetch function * Automatically handles Unix socket vs TCP based on UPSTREAM_URL */ -function createSmartFetch(upstreamUrl: string | undefined) { - if (!upstreamUrl) { - const error = "UPSTREAM_URL environment variable not set"; - logger.error(error); - throw new Error(error); - } - +function createSmartFetch(upstreamUrl: string) { const isUnixSocket = upstreamUrl.startsWith("/") || upstreamUrl.startsWith("./"); const baseUrl = isUnixSocket ? "http://localhost" : upstreamUrl; @@ -84,5 +78,20 @@ function createSmartFetch(upstreamUrl: string | undefined) { }; } -// Export the configured smart fetch function -export const apiFetch = createSmartFetch(env.UPSTREAM_URL); +// Lazy-initialized fetch function (only throws if UPSTREAM_URL is missing when actually used) +let cachedFetch: ReturnType | null = null; + +export async function apiFetch( + path: string, + options?: FetchOptions, +): Promise { + if (!cachedFetch) { + if (!env.UPSTREAM_URL) { + const error = "UPSTREAM_URL environment variable not set"; + logger.error(error); + throw new Error(error); + } + cachedFetch = createSmartFetch(env.UPSTREAM_URL); + } + return cachedFetch(path, options); +} diff --git a/web/src/lib/components/admin/Button.svelte b/web/src/lib/components/admin/Button.svelte index 1744c97..983f749 100644 --- a/web/src/lib/components/admin/Button.svelte +++ b/web/src/lib/components/admin/Button.svelte @@ -30,7 +30,7 @@ primary: "bg-admin-accent text-white hover:bg-admin-accent-hover focus-visible:ring-admin-accent shadow-sm hover:shadow", secondary: - "bg-transparent text-admin-text border border-admin-border hover:border-admin-border-hover hover:bg-admin-surface-hover/50 focus-visible:ring-admin-accent", + "bg-zinc-200/60 dark:bg-zinc-600/50 text-admin-text border border-zinc-400/50 dark:border-zinc-700 hover:border-zinc-400 dark:hover:border-zinc-500 hover:bg-zinc-300/70 dark:hover:bg-zinc-500/60 focus-visible:ring-admin-accent", danger: "bg-red-600 text-white hover:bg-red-500 focus-visible:ring-red-500 shadow-sm hover:shadow", ghost: diff --git a/web/src/lib/components/admin/Table.svelte b/web/src/lib/components/admin/Table.svelte index 9e1d445..c998e21 100644 --- a/web/src/lib/components/admin/Table.svelte +++ b/web/src/lib/components/admin/Table.svelte @@ -10,7 +10,7 @@
{@render children?.()} diff --git a/web/src/routes/+layout.server.ts b/web/src/routes/+layout.server.ts index 38d70f6..25c7c26 100644 --- a/web/src/routes/+layout.server.ts +++ b/web/src/routes/+layout.server.ts @@ -2,16 +2,34 @@ import type { LayoutServerLoad } from "./$types"; import { getOGImageUrl } from "$lib/og-types"; import { apiFetch } from "$lib/api.server"; import type { SiteSettings } from "$lib/admin-types"; +import { building } from "$app/environment"; + +const DEFAULT_SETTINGS: SiteSettings = { + identity: { + siteTitle: "xevion.dev", + displayName: "Ryan Walters", + occupation: "Software Engineer", + bio: "Software engineer and developer", + }, + socialLinks: [], +}; export const load: LayoutServerLoad = async ({ url, fetch }) => { - // Fetch site settings for all pages - const settings = await apiFetch("/api/settings", { fetch }); + let settings: SiteSettings; + + if (building) { + // During prerendering, use default settings (API isn't available) + settings = DEFAULT_SETTINGS; + } else { + // At runtime, fetch from API + settings = await apiFetch("/api/settings", { fetch }); + } return { settings, metadata: { title: settings.identity.siteTitle, - description: settings.identity.bio.split("\n")[0], // First line of bio + description: settings.identity.bio.split("\n")[0], ogImage: getOGImageUrl({ type: "index" }), url: url.toString(), }, diff --git a/web/src/routes/admin/+page.svelte b/web/src/routes/admin/+page.svelte index e37a2df..2802393 100644 --- a/web/src/routes/admin/+page.svelte +++ b/web/src/routes/admin/+page.svelte @@ -58,10 +58,10 @@

Recent Events diff --git a/web/src/routes/admin/projects/+page.svelte b/web/src/routes/admin/projects/+page.svelte index 1352749..d1002e7 100644 --- a/web/src/routes/admin/projects/+page.svelte +++ b/web/src/routes/admin/projects/+page.svelte @@ -108,7 +108,7 @@

{:else}
- + - + {#each projects as project (project.id)} - +
diff --git a/web/src/routes/admin/tags/+page.svelte b/web/src/routes/admin/tags/+page.svelte index cc8b9f0..158ea77 100644 --- a/web/src/routes/admin/tags/+page.svelte +++ b/web/src/routes/admin/tags/+page.svelte @@ -4,6 +4,7 @@ import Table from "$lib/components/admin/Table.svelte"; import Modal from "$lib/components/admin/Modal.svelte"; import ColorPicker from "$lib/components/admin/ColorPicker.svelte"; + import IconPicker from "$lib/components/admin/IconPicker.svelte"; import { getAdminTags, createAdminTag, @@ -25,6 +26,7 @@ let showCreateForm = $state(false); let createName = $state(""); let createSlug = $state(""); + let createIcon = $state(""); let createColor = $state(undefined); let creating = $state(false); @@ -32,6 +34,7 @@ let editingId = $state(null); let editName = $state(""); let editSlug = $state(""); + let editIcon = $state(""); let editColor = $state(undefined); let updating = $state(false); @@ -63,12 +66,14 @@ const data: CreateTagData = { name: createName, slug: createSlug || undefined, + icon: createIcon || undefined, color: createColor, }; await createAdminTag(data); await loadTags(); createName = ""; createSlug = ""; + createIcon = ""; createColor = undefined; showCreateForm = false; } catch (error) { @@ -83,6 +88,7 @@ editingId = tag.id; editName = tag.name; editSlug = tag.slug; + editIcon = tag.icon || ""; editColor = tag.color; } @@ -90,6 +96,7 @@ editingId = null; editName = ""; editSlug = ""; + editIcon = ""; editColor = undefined; } @@ -103,6 +110,7 @@ name: editName, slug: editSlug || undefined, color: editColor, + icon: editIcon || undefined, }; await updateAdminTag(data); await loadTags(); @@ -199,6 +207,9 @@ placeholder="Leave empty to auto-generate" />
+
+ +
@@ -229,7 +240,7 @@
{:else} - + + - + {#each tags as tag (tag.id)} - + {#if editingId === tag.id} + +
Slug + Icon + @@ -258,9 +274,9 @@
@@ -277,6 +293,17 @@ placeholder="tag-slug" /> + {#if editIcon} +
+
+ {editIcon} +
+
+ {:else} + No icon + {/if} +
{#if editColor}
@@ -323,6 +350,17 @@
{tag.slug} + {#if tag.icon} +
+
+ {tag.icon} +
+
+ {:else} + No icon + {/if} +
{#if tag.color}