Skip to content

Commit bb373bd

Browse files
committed
lint && format
1 parent 9fcaf5d commit bb373bd

10 files changed

Lines changed: 83 additions & 108 deletions

File tree

src/lib/components/markdown-editor.svelte

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { Tooltip, TooltipContent, TooltipTrigger, TooltipProvider } from "$lib/components/ui/tooltip";
55
import * as ToggleGroup from "$lib/components/ui/toggle-group";
66
import snarkdown from "snarkdown";
7-
7+
88
import Bold from "@lucide/svelte/icons/bold";
99
import Italic from "@lucide/svelte/icons/italic";
1010
import Heading from "@lucide/svelte/icons/heading";
@@ -15,7 +15,11 @@
1515
import Eye from "@lucide/svelte/icons/eye";
1616
import FileEdit from "@lucide/svelte/icons/file-edit";
1717
18-
let { value = $bindable(), id = undefined, placeholder = undefined } = $props<{
18+
let {
19+
value = $bindable(),
20+
id,
21+
placeholder,
22+
} = $props<{
1923
value?: string;
2024
id?: string;
2125
placeholder?: string;
@@ -30,9 +34,9 @@
3034
const start = textarea.selectionStart;
3135
const end = textarea.selectionEnd;
3236
const text = textarea.value;
33-
const selection = text.substring(start, end);
34-
const beforeText = text.substring(0, start);
35-
const afterText = text.substring(end);
37+
const selection = text.slice(start, end);
38+
const beforeText = text.slice(0, Math.max(0, start));
39+
const afterText = text.slice(Math.max(0, end));
3640
3741
value = beforeText + before + selection + after + afterText;
3842
@@ -56,29 +60,20 @@
5660
const lineStart = text.lastIndexOf("\n", start - 1) + 1;
5761
const lineEnd = text.indexOf("\n", end);
5862
const lineEndPos = lineEnd === -1 ? text.length : lineEnd;
59-
const line = text.substring(lineStart, lineEndPos);
63+
const line = text.slice(lineStart, lineEndPos);
6064
6165
// Regex to match existing header prefix (up to 5 # followed by a space)
6266
const headerMatch = line.match(/^(#{1,5})\s/);
63-
let newLine = line;
64-
65-
if (headerMatch && headerMatch[1]) {
66-
const hashes = headerMatch[1];
67-
const currentLevel = hashes.length;
68-
if (currentLevel < 5) {
69-
// Increment level: e.g. ## -> ###
70-
newLine = "#".repeat(currentLevel + 1) + " " + line.substring(hashes.length + 1);
71-
} else {
72-
// Level 5 reached, back to normal paragraph
73-
newLine = line.substring(hashes.length + 1);
74-
}
75-
} else {
76-
// No header, start with H1
77-
newLine = "# " + line;
78-
}
79-
80-
const beforeText = text.substring(0, lineStart);
81-
const afterText = text.substring(lineEndPos);
67+
const hashes = headerMatch?.[1];
68+
69+
const newLine = hashes
70+
? hashes.length < 5
71+
? "#".repeat(hashes.length + 1) + " " + line.slice(hashes.length + 1)
72+
: line.slice(hashes.length + 1)
73+
: "# " + line;
74+
75+
const beforeText = text.slice(0, Math.max(0, lineStart));
76+
const afterText = text.slice(Math.max(0, lineEndPos));
8277
8378
value = beforeText + newLine + afterText;
8479
@@ -108,7 +103,7 @@
108103
<div class="flex flex-wrap items-center justify-between border-b p-1">
109104
<div class="flex flex-wrap gap-1">
110105
<TooltipProvider>
111-
{#each actions as item}
106+
{#each actions as item (item.label)}
112107
<Tooltip>
113108
<TooltipTrigger>
114109
<Button
@@ -142,18 +137,20 @@
142137
</ToggleGroup.Item>
143138
</ToggleGroup.Root>
144139
</div>
145-
140+
146141
{#if view === "edit"}
147142
<Textarea
148143
bind:ref={textarea}
149144
bind:value
150-
id={id}
145+
{id}
151146
{placeholder}
152-
class="min-h-[300px] max-h-[400px] overflow-y-auto border-none focus-visible:ring-0"
147+
class="max-h-[400px] min-h-[300px] overflow-y-auto border-none focus-visible:ring-0"
153148
/>
154149
{:else}
155-
<div class="min-h-[300px] max-h-[400px] overflow-y-auto p-4">
156-
<div class="prose prose-sm dark:prose-invert max-w-none">
150+
<div class="max-h-[400px] min-h-[300px] overflow-y-auto p-4">
151+
<div class="prose prose-sm max-w-none dark:prose-invert">
152+
<!-- Using {@html} to render markdown as HTML. -->
153+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
157154
{@html html}
158155
</div>
159156
</div>
@@ -168,4 +165,3 @@
168165
box-shadow: none !important;
169166
}
170167
</style>
171-

src/lib/server/customisation/utils.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,16 @@ export function flattenCustomisation(custom: AppCustomisation | null): Customisa
4040
privacyPolicyEn: c.privacyPolicy?.en || DEFAULT_CUSTOMISATION.privacyPolicy.en,
4141
organizationRulesUrl: c.organizationRulesUrl || DEFAULT_CUSTOMISATION.organizationRulesUrl,
4242
memberResignRule: c.memberResignRule || DEFAULT_CUSTOMISATION.memberResignRule,
43-
memberResignDefaultReasonFi:
44-
c.memberResignDefaultReason?.fi || DEFAULT_CUSTOMISATION.memberResignDefaultReason.fi,
45-
memberResignDefaultReasonEn:
46-
c.memberResignDefaultReason?.en || DEFAULT_CUSTOMISATION.memberResignDefaultReason.en,
43+
memberResignDefaultReasonFi: c.memberResignDefaultReason?.fi || DEFAULT_CUSTOMISATION.memberResignDefaultReason.fi,
44+
memberResignDefaultReasonEn: c.memberResignDefaultReason?.en || DEFAULT_CUSTOMISATION.memberResignDefaultReason.en,
4745
};
4846
}
4947

5048
/**
5149
* Resizes an SVG to 32x32 by updating its viewBox and width/height attributes.
5250
*/
5351
export function resizeSvgTo32(buffer: Buffer): Buffer {
54-
let svg = buffer.toString("utf-8");
52+
let svg = buffer.toString("utf8");
5553

5654
const svgMatch = svg.match(/<svg([^>]*)>/i);
5755
if (!svgMatch) return buffer;
@@ -62,20 +60,20 @@ export function resizeSvgTo32(buffer: Buffer): Buffer {
6260
const wMatch = attrs.match(/width\s*=\s*['"]([^'"]+)['"]/i);
6361
const hMatch = attrs.match(/height\s*=\s*['"]([^'"]+)['"]/i);
6462
if (wMatch && hMatch) {
65-
const w = parseFloat(wMatch[1] as string);
66-
const h = parseFloat(hMatch[1] as string);
67-
if (!isNaN(w) && !isNaN(h)) {
63+
const w = Number.parseFloat(wMatch[1] as string);
64+
const h = Number.parseFloat(hMatch[1] as string);
65+
if (!Number.isNaN(w) && !Number.isNaN(h)) {
6866
attrs += ` viewBox="0 0 ${w} ${h}"`;
6967
}
7068
}
7169
}
7270

73-
attrs = attrs.replace(/\bwidth\s*=\s*['"][^'"]*['"]/gi, "");
74-
attrs = attrs.replace(/\bheight\s*=\s*['"][^'"]*['"]/gi, "");
71+
attrs = attrs.replaceAll(/\bwidth\s*=\s*['"][^'"]*['"]/gi, "");
72+
attrs = attrs.replaceAll(/\bheight\s*=\s*['"][^'"]*['"]/gi, "");
7573
attrs = attrs.trim() + ' width="32" height="32"';
7674
svg = svg.replace(svgMatch[0], `<svg ${attrs}>`);
7775

78-
return Buffer.from(svg, "utf-8");
76+
return Buffer.from(svg, "utf8");
7977
}
8078

8179
/**

src/lib/server/db/schema.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,7 @@ export const auditLog = pgTable("audit_log", {
177177
export const appCustomisation = pgTable("app_customisation", {
178178
id: integer().primaryKey().default(1),
179179
accentColor: text().notNull().default(DEFAULT_CUSTOMISATION.accentColor),
180-
organizationName: jsonb()
181-
.$type<LocalizedString>()
182-
.notNull()
183-
.default(DEFAULT_CUSTOMISATION.organizationName),
180+
organizationName: jsonb().$type<LocalizedString>().notNull().default(DEFAULT_CUSTOMISATION.organizationName),
184181
appName: jsonb().$type<LocalizedString>().notNull().default(DEFAULT_CUSTOMISATION.appName),
185182
logo: bytea(),
186183
logoDark: bytea(),
@@ -189,15 +186,10 @@ export const appCustomisation = pgTable("app_customisation", {
189186
businessId: text().notNull().default(DEFAULT_CUSTOMISATION.businessId),
190187
overseerContact: text().notNull().default(DEFAULT_CUSTOMISATION.overseerContact),
191188
overseerAddress: text().notNull().default(DEFAULT_CUSTOMISATION.overseerAddress),
192-
privacyPolicy: jsonb()
193-
.$type<LocalizedString>()
194-
.notNull()
195-
.default(DEFAULT_CUSTOMISATION.privacyPolicy),
189+
privacyPolicy: jsonb().$type<LocalizedString>().notNull().default(DEFAULT_CUSTOMISATION.privacyPolicy),
196190
organizationRulesUrl: text().notNull().default(DEFAULT_CUSTOMISATION.organizationRulesUrl),
197191
memberResignRule: text().default(DEFAULT_CUSTOMISATION.memberResignRule),
198-
memberResignDefaultReason: jsonb()
199-
.$type<LocalizedString>()
200-
.default(DEFAULT_CUSTOMISATION.memberResignDefaultReason),
192+
memberResignDefaultReason: jsonb().$type<LocalizedString>().default(DEFAULT_CUSTOMISATION.memberResignDefaultReason),
201193
...timestamps,
202194
});
203195

src/lib/server/emails/types.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,7 @@ export interface EmailTemplate<TMetadata = Record<string, unknown>> {
1212
type: EmailType;
1313

1414
// Generate email content
15-
render(
16-
locale: "fi" | "en",
17-
metadata: TMetadata,
18-
LL: TranslationFunctions,
19-
organizationName: string,
20-
): EmailContent;
15+
render(locale: "fi" | "en", metadata: TMetadata, LL: TranslationFunctions, organizationName: string): EmailContent;
2116

2217
// For future: batching, deduplication config
2318
// (keep interface extensible but don't implement yet)

src/routes/[locale=locale]/(app)/admin/customise/+page.server.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,14 +94,8 @@ export const actions: Actions = {
9494
organizationRulesUrl: validated.output.organizationRulesUrl || existing.organizationRulesUrl,
9595
memberResignRule: validated.output.memberResignRule || existing.memberResignRule,
9696
memberResignDefaultReason: {
97-
fi:
98-
validated.output.memberResignDefaultReasonFi ||
99-
existing.memberResignDefaultReason?.fi ||
100-
"",
101-
en:
102-
validated.output.memberResignDefaultReasonEn ||
103-
existing.memberResignDefaultReason?.en ||
104-
"",
97+
fi: validated.output.memberResignDefaultReasonFi || existing.memberResignDefaultReason?.fi || "",
98+
en: validated.output.memberResignDefaultReasonEn || existing.memberResignDefaultReason?.en || "",
10599
},
106100
logo: dbLogo ?? (removeLogo ? null : existing.logo),
107101
logoDark: dbLogoDark ?? (removeLogoDark ? null : existing.logoDark),
@@ -113,13 +107,12 @@ export const actions: Actions = {
113107
// Upsert logic: check if record with ID 1 exists
114108
const [record] = await db.select().from(table.appCustomisation).where(eq(table.appCustomisation.id, 1)).limit(1);
115109

116-
await (record ? db
117-
.update(table.appCustomisation)
118-
.set(updateData)
119-
.where(eq(table.appCustomisation.id, 1)) : db.insert(table.appCustomisation).values({
120-
id: 1,
121-
...updateData,
122-
}));
110+
await (record
111+
? db.update(table.appCustomisation).set(updateData).where(eq(table.appCustomisation.id, 1))
112+
: db.insert(table.appCustomisation).values({
113+
id: 1,
114+
...updateData,
115+
}));
123116

124117
await updateCustomisationCache();
125118
event.locals.customisations = await getCustomisations();

src/routes/[locale=locale]/(app)/admin/customise/+page.svelte

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,9 @@
254254
class="max-w-md"
255255
/>
256256
</div>
257-
{#if errors.memberResignDefaultReasonFi}<p class="mt-2 text-sm text-red-600">{errors.memberResignDefaultReasonFi}</p>{/if}
257+
{#if errors.memberResignDefaultReasonFi}<p class="mt-2 text-sm text-red-600">
258+
{errors.memberResignDefaultReasonFi}
259+
</p>{/if}
258260
</div>
259261

260262
<div class="sm:col-span-1">
@@ -270,7 +272,9 @@
270272
class="max-w-md"
271273
/>
272274
</div>
273-
{#if errors.memberResignDefaultReasonEn}<p class="mt-2 text-sm text-red-600">{errors.memberResignDefaultReasonEn}</p>{/if}
275+
{#if errors.memberResignDefaultReasonEn}<p class="mt-2 text-sm text-red-600">
276+
{errors.memberResignDefaultReasonEn}
277+
</p>{/if}
274278
</div>
275279
</div>
276280
</div>

src/routes/[locale=locale]/(app)/admin/customise/schema.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,7 @@ export const updateCustomisationSchema = v.object({
1111
overseerAddress: v.optional(v.string()),
1212
privacyPolicyFi: v.pipe(v.string(), v.minLength(1)),
1313
privacyPolicyEn: v.pipe(v.string(), v.minLength(1)),
14-
organizationRulesUrl: v.optional(
15-
v.union([
16-
v.literal(""),
17-
v.pipe(v.string(), v.url()),
18-
]),
19-
),
14+
organizationRulesUrl: v.optional(v.union([v.literal(""), v.pipe(v.string(), v.url())])),
2015
memberResignRule: v.optional(v.string()),
2116
memberResignDefaultReasonFi: v.optional(v.string()),
2217
memberResignDefaultReasonEn: v.optional(v.string()),

src/routes/[locale=locale]/(app)/admin/members/members-table.svelte

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,28 +1142,28 @@
11421142
</Button>
11431143
</div>
11441144
{:else if membership.status === "active"}
1145-
<div class="flex gap-2 border-t pt-3">
1146-
<Button
1147-
size="sm"
1148-
variant="outline"
1149-
onclick={() => openIndividualAction("deemResigned", membership.id, memberName)}
1150-
>
1151-
{$LL.admin.members.table.deemResigned() +
1152-
" (" +
1153-
page.data.customisations.memberResignRule +
1154-
")"}
1155-
</Button>
1156-
<Button
1157-
size="sm"
1158-
variant="destructive"
1159-
onclick={() => openIndividualAction("resign", membership.id, memberName)}
1160-
>
1161-
{$LL.admin.members.table.resignMembership() +
1162-
" (" +
1163-
page.data.customisations.memberResignRule +
1164-
")"}
1165-
</Button>
1166-
</div>
1145+
<div class="flex gap-2 border-t pt-3">
1146+
<Button
1147+
size="sm"
1148+
variant="outline"
1149+
onclick={() => openIndividualAction("deemResigned", membership.id, memberName)}
1150+
>
1151+
{$LL.admin.members.table.deemResigned() +
1152+
" (" +
1153+
page.data.customisations.memberResignRule +
1154+
")"}
1155+
</Button>
1156+
<Button
1157+
size="sm"
1158+
variant="destructive"
1159+
onclick={() => openIndividualAction("resign", membership.id, memberName)}
1160+
>
1161+
{$LL.admin.members.table.resignMembership() +
1162+
" (" +
1163+
page.data.customisations.memberResignRule +
1164+
")"}
1165+
</Button>
1166+
</div>
11671167
{/if}
11681168
{/if}
11691169
</div>

src/routes/[locale=locale]/(public)/privacy-policy/+page.svelte

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
import { page } from "$app/state";
44
55
const appName = $derived(page.data.customisations?.appName?.[$locale] ?? $LL.app.title());
6-
6+
77
// Custom content from DB (if any)
88
const fiCustom = $derived(page.data.customisations?.privacyPolicy?.fi);
99
const enCustom = $derived(page.data.customisations?.privacyPolicy?.en);
10-
10+
1111
// Only use custom if it's not the default placeholder
1212
const hasCustom = $derived(
1313
($locale === "fi" && fiCustom && fiCustom !== "Rekisteri- ja tietosuojaseloste") ||
14-
($locale === "en" && enCustom && enCustom !== "Privacy Policy")
14+
($locale === "en" && enCustom && enCustom !== "Privacy Policy"),
1515
);
1616
1717
const customContent = $derived($locale === "fi" ? fiCustom : enCustom);

src/routes/[locale=locale]/+layout.svelte

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
<link rel="icon" href="/api/image/faviconDark" media="(prefers-color-scheme: dark)" />
2222
{/if}
2323
{#if data.customisations?.accentColor}
24+
<!-- Inject Accent Color value as primary. Only admin can set and it's validated as an RGB hex -->
25+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
2426
{@html `<style>:root { --primary: ${data.customisations.accentColor}; }</style>`}
2527
{/if}
2628
</svelte:head>

0 commit comments

Comments
 (0)