mirror of
https://github.com/dsec-hub/dsec-notebook.git
synced 2026-09-22 07:24:27 +00:00
change title from Notebook to DSEC Notebook
This commit is contained in:
parent
c135b017fb
commit
80833ad8f4
11 changed files with 227 additions and 132 deletions
|
|
@ -111,7 +111,11 @@
|
||||||
passwordError = "";
|
passwordError = "";
|
||||||
passwordMessage = "";
|
passwordMessage = "";
|
||||||
try {
|
try {
|
||||||
await mutation("users:changePassword", { token, currentPassword, newPassword });
|
await mutation("users:changePassword", {
|
||||||
|
token,
|
||||||
|
currentPassword,
|
||||||
|
newPassword,
|
||||||
|
});
|
||||||
currentPassword = "";
|
currentPassword = "";
|
||||||
newPassword = "";
|
newPassword = "";
|
||||||
confirmPassword = "";
|
confirmPassword = "";
|
||||||
|
|
@ -131,7 +135,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Account — Notebook</title>
|
<title>Account — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
@ -202,8 +206,12 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if profileError}<p class="text-primary text-sm">{profileError}</p>{/if}
|
{#if profileError}<p class="text-primary text-sm">
|
||||||
{#if profileMessage}<p class="text-secondary text-sm">{profileMessage}</p>{/if}
|
{profileError}
|
||||||
|
</p>{/if}
|
||||||
|
{#if profileMessage}<p class="text-secondary text-sm">
|
||||||
|
{profileMessage}
|
||||||
|
</p>{/if}
|
||||||
<button type="submit" disabled={profileSaving} class="btn-primary">
|
<button type="submit" disabled={profileSaving} class="btn-primary">
|
||||||
{profileSaving ? "Saving..." : "Save profile"}
|
{profileSaving ? "Saving..." : "Save profile"}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -250,8 +258,12 @@
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{#if passwordError}<p class="text-primary text-sm">{passwordError}</p>{/if}
|
{#if passwordError}<p class="text-primary text-sm">
|
||||||
{#if passwordMessage}<p class="text-secondary text-sm">{passwordMessage}</p>{/if}
|
{passwordError}
|
||||||
|
</p>{/if}
|
||||||
|
{#if passwordMessage}<p class="text-secondary text-sm">
|
||||||
|
{passwordMessage}
|
||||||
|
</p>{/if}
|
||||||
<button type="submit" disabled={passwordSaving} class="btn-primary">
|
<button type="submit" disabled={passwordSaving} class="btn-primary">
|
||||||
{passwordSaving ? "Changing..." : "Change password"}
|
{passwordSaving ? "Changing..." : "Change password"}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,12 @@
|
||||||
type WeekStat = { weekStart: number; notes: number; questions: number };
|
type WeekStat = { weekStart: number; notes: number; questions: number };
|
||||||
type Stats = {
|
type Stats = {
|
||||||
weeks: WeekStat[];
|
weeks: WeekStat[];
|
||||||
totals: { notes: number; questions: number; users: number; units: number };
|
totals: {
|
||||||
|
notes: number;
|
||||||
|
questions: number;
|
||||||
|
users: number;
|
||||||
|
units: number;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
type AdminUser = UserDoc & { noteCount: number; questionCount: number };
|
type AdminUser = UserDoc & { noteCount: number; questionCount: number };
|
||||||
|
|
@ -54,18 +59,33 @@
|
||||||
let pageError = $state("");
|
let pageError = $state("");
|
||||||
|
|
||||||
// Unit editor
|
// Unit editor
|
||||||
let unitForm = $state({ id: "", code: "", code2: "", name: "", description: "" });
|
let unitForm = $state({
|
||||||
|
id: "",
|
||||||
|
code: "",
|
||||||
|
code2: "",
|
||||||
|
name: "",
|
||||||
|
description: "",
|
||||||
|
});
|
||||||
let unitBusy = $state(false);
|
let unitBusy = $state(false);
|
||||||
let unitError = $state("");
|
let unitError = $state("");
|
||||||
let unitSuccess = $state("");
|
let unitSuccess = $state("");
|
||||||
|
|
||||||
// Account editor
|
// Account editor
|
||||||
let userForm = $state<{ id: string; email: string; name: string; role: string } | null>(null);
|
let userForm = $state<{
|
||||||
|
id: string;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
role: string;
|
||||||
|
} | null>(null);
|
||||||
let userBusy = $state(false);
|
let userBusy = $state(false);
|
||||||
let userError = $state("");
|
let userError = $state("");
|
||||||
|
|
||||||
// Note editor
|
// Note editor
|
||||||
let noteForm = $state<{ id: string; title: string; content: string } | null>(null);
|
let noteForm = $state<{
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
} | null>(null);
|
||||||
let noteBusy = $state(false);
|
let noteBusy = $state(false);
|
||||||
let noteError = $state("");
|
let noteError = $state("");
|
||||||
|
|
||||||
|
|
@ -207,7 +227,10 @@
|
||||||
unitError = "";
|
unitError = "";
|
||||||
unitSuccess = "";
|
unitSuccess = "";
|
||||||
try {
|
try {
|
||||||
await mutation("admin:unitsDelete", { token: adminToken(), id: unit._id });
|
await mutation("admin:unitsDelete", {
|
||||||
|
token: adminToken(),
|
||||||
|
id: unit._id,
|
||||||
|
});
|
||||||
units = (await query("units:getAll")) as UnitDoc[];
|
units = (await query("units:getAll")) as UnitDoc[];
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
unitError = err.message ?? "Failed to delete unit";
|
unitError = err.message ?? "Failed to delete unit";
|
||||||
|
|
@ -217,7 +240,12 @@
|
||||||
// ---- accounts ----
|
// ---- accounts ----
|
||||||
|
|
||||||
function startEditUser(user: AdminUser) {
|
function startEditUser(user: AdminUser) {
|
||||||
userForm = { id: user._id, email: user.email, name: user.name, role: user.role };
|
userForm = {
|
||||||
|
id: user._id,
|
||||||
|
email: user.email,
|
||||||
|
name: user.name,
|
||||||
|
role: user.role,
|
||||||
|
};
|
||||||
userError = "";
|
userError = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -239,7 +267,9 @@
|
||||||
name: userForm.name,
|
name: userForm.name,
|
||||||
role: userForm.role,
|
role: userForm.role,
|
||||||
});
|
});
|
||||||
users = (await query("admin:usersList", { token: adminToken() })) as AdminUser[];
|
users = (await query("admin:usersList", {
|
||||||
|
token: adminToken(),
|
||||||
|
})) as AdminUser[];
|
||||||
resetUserForm();
|
resetUserForm();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
userError = err.message ?? "Failed to update account";
|
userError = err.message ?? "Failed to update account";
|
||||||
|
|
@ -258,7 +288,9 @@
|
||||||
name: user.name,
|
name: user.name,
|
||||||
role: user.role === "admin" ? "user" : "admin",
|
role: user.role === "admin" ? "user" : "admin",
|
||||||
});
|
});
|
||||||
users = (await query("admin:usersList", { token: adminToken() })) as AdminUser[];
|
users = (await query("admin:usersList", {
|
||||||
|
token: adminToken(),
|
||||||
|
})) as AdminUser[];
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
userError = err.message ?? "Failed to change role";
|
userError = err.message ?? "Failed to change role";
|
||||||
}
|
}
|
||||||
|
|
@ -268,8 +300,13 @@
|
||||||
if (!confirm(`Delete ${user.email} and all of their content?`)) return;
|
if (!confirm(`Delete ${user.email} and all of their content?`)) return;
|
||||||
userError = "";
|
userError = "";
|
||||||
try {
|
try {
|
||||||
await mutation("admin:usersDelete", { token: adminToken(), id: user._id });
|
await mutation("admin:usersDelete", {
|
||||||
users = (await query("admin:usersList", { token: adminToken() })) as AdminUser[];
|
token: adminToken(),
|
||||||
|
id: user._id,
|
||||||
|
});
|
||||||
|
users = (await query("admin:usersList", {
|
||||||
|
token: adminToken(),
|
||||||
|
})) as AdminUser[];
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
userError = err.message ?? "Failed to delete account";
|
userError = err.message ?? "Failed to delete account";
|
||||||
}
|
}
|
||||||
|
|
@ -299,7 +336,9 @@
|
||||||
title: noteForm.title,
|
title: noteForm.title,
|
||||||
content: noteForm.content,
|
content: noteForm.content,
|
||||||
});
|
});
|
||||||
notes = (await query("admin:notesList", { token: adminToken() })) as AdminNote[];
|
notes = (await query("admin:notesList", {
|
||||||
|
token: adminToken(),
|
||||||
|
})) as AdminNote[];
|
||||||
resetNoteForm();
|
resetNoteForm();
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
noteError = err.message ?? "Failed to update note";
|
noteError = err.message ?? "Failed to update note";
|
||||||
|
|
@ -312,8 +351,13 @@
|
||||||
if (!confirm(`Delete note "${note.title}"?`)) return;
|
if (!confirm(`Delete note "${note.title}"?`)) return;
|
||||||
noteError = "";
|
noteError = "";
|
||||||
try {
|
try {
|
||||||
await mutation("admin:notesDelete", { token: adminToken(), id: note._id });
|
await mutation("admin:notesDelete", {
|
||||||
notes = (await query("admin:notesList", { token: adminToken() })) as AdminNote[];
|
token: adminToken(),
|
||||||
|
id: note._id,
|
||||||
|
});
|
||||||
|
notes = (await query("admin:notesList", {
|
||||||
|
token: adminToken(),
|
||||||
|
})) as AdminNote[];
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
noteError = err.message ?? "Failed to delete note";
|
noteError = err.message ?? "Failed to delete note";
|
||||||
}
|
}
|
||||||
|
|
@ -321,7 +365,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Admin — Notebook</title>
|
<title>Admin — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
@ -473,25 +517,35 @@
|
||||||
<div class="grid grid-cols-2 gap-4 py-8 lg:grid-cols-4">
|
<div class="grid grid-cols-2 gap-4 py-8 lg:grid-cols-4">
|
||||||
<div class="border-rule border p-5">
|
<div class="border-rule border p-5">
|
||||||
<p class="kicker">Notes</p>
|
<p class="kicker">Notes</p>
|
||||||
<p class="text-ink mt-2 font-serif text-4xl">{stats.totals.notes}</p>
|
<p class="text-ink mt-2 font-serif text-4xl">
|
||||||
|
{stats.totals.notes}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-rule border p-5">
|
<div class="border-rule border p-5">
|
||||||
<p class="kicker">Questions</p>
|
<p class="kicker">Questions</p>
|
||||||
<p class="text-ink mt-2 font-serif text-4xl">{stats.totals.questions}</p>
|
<p class="text-ink mt-2 font-serif text-4xl">
|
||||||
|
{stats.totals.questions}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-rule border p-5">
|
<div class="border-rule border p-5">
|
||||||
<p class="kicker">Accounts</p>
|
<p class="kicker">Accounts</p>
|
||||||
<p class="text-ink mt-2 font-serif text-4xl">{stats.totals.users}</p>
|
<p class="text-ink mt-2 font-serif text-4xl">
|
||||||
|
{stats.totals.users}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-rule border p-5">
|
<div class="border-rule border p-5">
|
||||||
<p class="kicker">Units</p>
|
<p class="kicker">Units</p>
|
||||||
<p class="text-ink mt-2 font-serif text-4xl">{stats.totals.units}</p>
|
<p class="text-ink mt-2 font-serif text-4xl">
|
||||||
|
{stats.totals.units}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="border-rule border-t pt-8">
|
<div class="border-rule border-t pt-8">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<p class="kicker">Posts — last {stats.weeks.length} weeks</p>
|
<p class="kicker">
|
||||||
|
Posts — last {stats.weeks.length} weeks
|
||||||
|
</p>
|
||||||
<div class="flex gap-4 text-[11px] tracking-[0.14em] uppercase">
|
<div class="flex gap-4 text-[11px] tracking-[0.14em] uppercase">
|
||||||
<span class="text-ink"><span class="text-primary">■</span> Notes</span>
|
<span class="text-ink"><span class="text-primary">■</span> Notes</span>
|
||||||
<span class="text-ink"
|
<span class="text-ink"
|
||||||
|
|
@ -540,7 +594,9 @@
|
||||||
<p class="text-ink font-medium">
|
<p class="text-ink font-medium">
|
||||||
{unit.code}{unit.code2 ? ` / ${unit.code2}` : ""}
|
{unit.code}{unit.code2 ? ` / ${unit.code2}` : ""}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-muted truncate text-sm">{unit.name}</p>
|
<p class="text-muted truncate text-sm">
|
||||||
|
{unit.name}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex shrink-0 gap-3">
|
<div class="flex shrink-0 gap-3">
|
||||||
<button
|
<button
|
||||||
|
|
@ -567,7 +623,9 @@
|
||||||
onsubmit={saveUnit}
|
onsubmit={saveUnit}
|
||||||
class="border-rule sticky top-5 h-fit space-y-5 border p-5"
|
class="border-rule sticky top-5 h-fit space-y-5 border p-5"
|
||||||
>
|
>
|
||||||
<p class="kicker">{unitForm.id ? "Edit unit" : "Add unit"}</p>
|
<p class="kicker">
|
||||||
|
{unitForm.id ? "Edit unit" : "Add unit"}
|
||||||
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<label for="unit-code" class="kicker mb-2 block">Code</label>
|
<label for="unit-code" class="kicker mb-2 block">Code</label>
|
||||||
<input
|
<input
|
||||||
|
|
@ -643,8 +701,12 @@
|
||||||
<div class="border-rule border-b py-3">
|
<div class="border-rule border-b py-3">
|
||||||
<div class="flex items-center justify-between gap-4">
|
<div class="flex items-center justify-between gap-4">
|
||||||
<div class="min-w-0">
|
<div class="min-w-0">
|
||||||
<p class="text-ink font-medium">{user.name}</p>
|
<p class="text-ink font-medium">
|
||||||
<p class="text-muted truncate text-sm">{user.email}</p>
|
{user.name}
|
||||||
|
</p>
|
||||||
|
<p class="text-muted truncate text-sm">
|
||||||
|
{user.email}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<span class="chip {user.role === 'admin' ? 'chip-active' : ''}">
|
<span class="chip {user.role === 'admin' ? 'chip-active' : ''}">
|
||||||
{user.role}
|
{user.role}
|
||||||
|
|
@ -765,7 +827,9 @@
|
||||||
<div class="border-rule border-b py-3">
|
<div class="border-rule border-b py-3">
|
||||||
<div class="flex items-start justify-between gap-4">
|
<div class="flex items-start justify-between gap-4">
|
||||||
<div class="min-w-0">
|
<div class="min-w-0">
|
||||||
<p class="text-ink font-medium">{note.title}</p>
|
<p class="text-ink font-medium">
|
||||||
|
{note.title}
|
||||||
|
</p>
|
||||||
<p class="text-muted mt-1 truncate text-sm">
|
<p class="text-muted mt-1 truncate text-sm">
|
||||||
{note.unitCode
|
{note.unitCode
|
||||||
? note.unitCode +
|
? note.unitCode +
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Sign in — Notebook</title>
|
<title>Sign in — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="page flex min-h-[calc(100vh-220px)] items-center">
|
<div class="page flex min-h-[calc(100vh-220px)] items-center">
|
||||||
|
|
@ -302,8 +302,7 @@
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else if !sent}
|
||||||
{#if !sent}
|
|
||||||
<form onsubmit={handleSubmit} class="border-rule space-y-5 border-t pt-8">
|
<form onsubmit={handleSubmit} class="border-rule space-y-5 border-t pt-8">
|
||||||
<div>
|
<div>
|
||||||
<label for="email" class="kicker mb-2 block">Deakin email</label>
|
<label for="email" class="kicker mb-2 block">Deakin email</label>
|
||||||
|
|
@ -340,8 +339,8 @@
|
||||||
{:else}
|
{:else}
|
||||||
<form onsubmit={handleSubmit} class="border-rule space-y-5 border-t pt-8">
|
<form onsubmit={handleSubmit} class="border-rule space-y-5 border-t pt-8">
|
||||||
<p class="text-muted text-sm">
|
<p class="text-muted text-sm">
|
||||||
We sent a 6-digit code to <span class="text-ink">{email}</span>. It expires
|
We sent a 6-digit code to <span class="text-ink">{email}</span>. It expires in
|
||||||
in 10 minutes.
|
10 minutes.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -380,15 +379,10 @@
|
||||||
{loading ? "Resetting..." : "Reset password"}
|
{loading ? "Resetting..." : "Reset password"}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button type="button" onclick={goBack} class="text-muted hover:text-ink text-sm">
|
||||||
type="button"
|
|
||||||
onclick={goBack}
|
|
||||||
class="text-muted hover:text-ink text-sm"
|
|
||||||
>
|
|
||||||
Use a different email
|
Use a different email
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,10 @@
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const [n, u] = await Promise.all([query("notes:list", {}), query("units:getAll")]);
|
const [n, u] = await Promise.all([query("notes:list", {}), query("units:getAll")]);
|
||||||
const unitMap = new Map((u as UnitDoc[]).map((unit) => [unit._id, unit]));
|
const unitMap = new Map((u as UnitDoc[]).map((unit) => [unit._id, unit]));
|
||||||
notes = (n as NoteDoc[]).map((note) => ({ ...note, unit: unitMap.get(note.unitId) }));
|
notes = (n as NoteDoc[]).map((note) => ({
|
||||||
|
...note,
|
||||||
|
unit: unitMap.get(note.unitId),
|
||||||
|
}));
|
||||||
units = u as UnitDoc[];
|
units = u as UnitDoc[];
|
||||||
loading = false;
|
loading = false;
|
||||||
});
|
});
|
||||||
|
|
@ -43,7 +46,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Notes — Notebook</title>
|
<title>Notes — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Post a note — Notebook</title>
|
<title>Post a note — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<svelte:window onbeforeunload={handleBeforeUnload} />
|
<svelte:window onbeforeunload={handleBeforeUnload} />
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Ask a question — Notebook</title>
|
<title>Ask a question — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<svelte:window onbeforeunload={handleBeforeUnload} />
|
<svelte:window onbeforeunload={handleBeforeUnload} />
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Questions — Notebook</title>
|
<title>Questions — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@
|
||||||
searchQuery = new URL(window.location.href).searchParams.get("q") ?? "";
|
searchQuery = new URL(window.location.href).searchParams.get("q") ?? "";
|
||||||
if (searchQuery) {
|
if (searchQuery) {
|
||||||
ranQuery = true;
|
ranQuery = true;
|
||||||
results = (await query("search:all", { query: searchQuery })) as SearchResult[];
|
results = (await query("search:all", {
|
||||||
|
query: searchQuery,
|
||||||
|
})) as SearchResult[];
|
||||||
}
|
}
|
||||||
loading = false;
|
loading = false;
|
||||||
});
|
});
|
||||||
|
|
@ -25,7 +27,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{searchQuery ? `Search: ${searchQuery}` : "Search"} — Notebook</title>
|
<title>{searchQuery ? `Search: ${searchQuery}` : "Search"} — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
@ -110,7 +112,9 @@
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
{#if unitResults.length === 0}
|
{#if unitResults.length === 0}
|
||||||
<p class="text-muted text-sm">No results found for “{searchQuery}”.</p>
|
<p class="text-muted text-sm">
|
||||||
|
No results found for “{searchQuery}”.
|
||||||
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -40,15 +40,19 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{topic?.name ?? "Topic"} — Notebook</title>
|
<title>{topic?.name ?? "Topic"} — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<p class="kicker py-16">Loading</p>
|
<p class="kicker py-16">Loading</p>
|
||||||
{:else if topic}
|
{:else if topic}
|
||||||
<p class="kicker"><a href="/" class="hover:text-primary">Home</a> · Topic</p>
|
<p class="kicker">
|
||||||
<h1 class="text-ink mt-2 font-serif text-4xl font-medium">{topic.name}</h1>
|
<a href="/" class="hover:text-primary">Home</a> · Topic
|
||||||
|
</p>
|
||||||
|
<h1 class="text-ink mt-2 font-serif text-4xl font-medium">
|
||||||
|
{topic.name}
|
||||||
|
</h1>
|
||||||
{#if topic.description}
|
{#if topic.description}
|
||||||
<p class="text-muted mt-2 text-[15px]">{topic.description}</p>
|
<p class="text-muted mt-2 text-[15px]">{topic.description}</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>Units — Notebook</title>
|
<title>Units — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
@ -47,7 +47,9 @@
|
||||||
{:else if units.length === 0}
|
{:else if units.length === 0}
|
||||||
<p class="text-muted py-16 text-sm">No units yet.</p>
|
<p class="text-muted py-16 text-sm">No units yet.</p>
|
||||||
{:else if visible.length === 0}
|
{:else if visible.length === 0}
|
||||||
<p class="text-muted py-16 text-sm">No units match “{searchQuery.trim()}”.</p>
|
<p class="text-muted py-16 text-sm">
|
||||||
|
No units match “{searchQuery.trim()}”.
|
||||||
|
</p>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="grid grid-cols-1 gap-3 pt-6 sm:grid-cols-2 lg:grid-cols-3">
|
<div class="grid grid-cols-1 gap-3 pt-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
{#each visible as unit (unit._id)}
|
{#each visible as unit (unit._id)}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,9 @@
|
||||||
tab = "posts";
|
tab = "posts";
|
||||||
|
|
||||||
void (async () => {
|
void (async () => {
|
||||||
const result = (await query("users:getPublicProfile", { id })) as Profile | null;
|
const result = (await query("users:getPublicProfile", {
|
||||||
|
id,
|
||||||
|
})) as Profile | null;
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
profile = result;
|
profile = result;
|
||||||
loading = false;
|
loading = false;
|
||||||
|
|
@ -60,7 +62,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>{profile?.name ?? "Profile"} — Notebook</title>
|
<title>{profile?.name ?? "Profile"} — DSEC Notebook</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
@ -71,7 +73,9 @@
|
||||||
<Avatar src={profile.avatarUrl} name={profile.name} size="xl" />
|
<Avatar src={profile.avatarUrl} name={profile.name} size="xl" />
|
||||||
<div>
|
<div>
|
||||||
<p class="kicker">Contributor</p>
|
<p class="kicker">Contributor</p>
|
||||||
<h1 class="text-ink mt-2 font-serif text-4xl font-medium">{profile.name}</h1>
|
<h1 class="text-ink mt-2 font-serif text-4xl font-medium">
|
||||||
|
{profile.name}
|
||||||
|
</h1>
|
||||||
<p class="text-muted mt-2 text-sm">
|
<p class="text-muted mt-2 text-sm">
|
||||||
Member since {new Date(profile._creationTime).toLocaleDateString(undefined, {
|
Member since {new Date(profile._creationTime).toLocaleDateString(undefined, {
|
||||||
month: "long",
|
month: "long",
|
||||||
|
|
@ -83,7 +87,9 @@
|
||||||
|
|
||||||
<section class="border-rule mt-10 grid grid-cols-2 border-y sm:grid-cols-4">
|
<section class="border-rule mt-10 grid grid-cols-2 border-y sm:grid-cols-4">
|
||||||
<div class="border-rule border-r px-4 py-5 first:pl-0">
|
<div class="border-rule border-r px-4 py-5 first:pl-0">
|
||||||
<p class="text-ink font-serif text-3xl">{profile.totalContributions}</p>
|
<p class="text-ink font-serif text-3xl">
|
||||||
|
{profile.totalContributions}
|
||||||
|
</p>
|
||||||
<p class="kicker text-muted mt-1">Contributions</p>
|
<p class="kicker text-muted mt-1">Contributions</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-rule border-r px-4 py-5">
|
<div class="border-rule border-r px-4 py-5">
|
||||||
|
|
@ -91,11 +97,15 @@
|
||||||
<p class="kicker text-muted mt-1">Notes</p>
|
<p class="kicker text-muted mt-1">Notes</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-rule border-r px-4 py-5">
|
<div class="border-rule border-r px-4 py-5">
|
||||||
<p class="text-ink font-serif text-3xl">{profile.questionCount}</p>
|
<p class="text-ink font-serif text-3xl">
|
||||||
|
{profile.questionCount}
|
||||||
|
</p>
|
||||||
<p class="kicker text-muted mt-1">Questions</p>
|
<p class="kicker text-muted mt-1">Questions</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="px-4 py-5">
|
<div class="px-4 py-5">
|
||||||
<p class="text-ink font-serif text-3xl">{profile.commentCount}</p>
|
<p class="text-ink font-serif text-3xl">
|
||||||
|
{profile.commentCount}
|
||||||
|
</p>
|
||||||
<p class="kicker text-muted mt-1">Comments</p>
|
<p class="kicker text-muted mt-1">Comments</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -161,7 +171,9 @@
|
||||||
<p class="text-ink mt-2 text-sm leading-relaxed">
|
<p class="text-ink mt-2 text-sm leading-relaxed">
|
||||||
{previewMarkdown(comment.content)}
|
{previewMarkdown(comment.content)}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-faint mt-1 text-xs">{timeAgo(comment.createdAt)}</p>
|
<p class="text-faint mt-1 text-xs">
|
||||||
|
{timeAgo(comment.createdAt)}
|
||||||
|
</p>
|
||||||
</article>
|
</article>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue