Release 202505261445

This commit is contained in:
pluja
2025-05-26 14:45:22 +00:00
parent f2021a3027
commit ba809840c6
19 changed files with 910 additions and 486 deletions

View File

@@ -1,5 +1,6 @@
---
import { Icon } from 'astro-icon/components'
import { Markdown } from 'astro-remote'
import { actions, isInputError } from 'astro:actions'
import { orderBy } from 'lodash-es'
@@ -76,6 +77,15 @@ const verificationStepUpdateInputErrors = isInputError(verificationStepUpdateRes
const verificationStepDeleteResult = Astro.getActionResult(actions.admin.verificationStep.delete)
Astro.locals.banners.addIfSuccess(verificationStepDeleteResult, 'Verification step deleted successfully')
const internalNoteCreateResult = Astro.getActionResult(actions.admin.service.internalNote.add)
Astro.locals.banners.addIfSuccess(internalNoteCreateResult, 'Internal note added successfully')
const internalNoteInputErrors = isInputError(internalNoteCreateResult?.error)
? internalNoteCreateResult.error.fields
: {}
const internalNoteDeleteResult = Astro.getActionResult(actions.admin.service.internalNote.delete)
Astro.locals.banners.addIfSuccess(internalNoteDeleteResult, 'Internal note deleted successfully')
const [service, categories, attributes] = await Astro.locals.banners.tryMany([
[
'Error fetching service',
@@ -130,6 +140,20 @@ const [service, categories, attributes] = await Astro.locals.banners.tryMany([
label: 'asc',
},
},
internalNotes: {
include: {
addedByUser: {
select: {
name: true,
displayName: true,
picture: true,
},
},
},
orderBy: {
createdAt: 'desc',
},
},
_count: {
select: {
verificationRequests: true,
@@ -290,13 +314,14 @@ if (!service) return Astro.rewrite('/404')
</div>
<InputText
label="Referral Code/Link"
label="Referral link path"
name="referral"
inputProps={{
value: service.referral ?? undefined,
placeholder: 'e.g., REFCODE123 or https://example.com?ref=123',
value: service.referral,
placeholder: 'e.g. ?ref=123 or /ref/123',
}}
error={serviceInputErrors.referral}
description="Will be appended to the service URL"
/>
<div class="flex items-center justify-between gap-2">
@@ -448,6 +473,108 @@ if (!service) return Astro.rewrite('/404')
</form>
</FormSection>
<FormSection title="Internal Notes" id="internal-notes">
<FormSubSection title="Existing Notes">
{
service.internalNotes.length === 0 ? (
<p class="border-night-600 bg-night-800 text-day-300 rounded-xl border p-6 text-center">
No internal notes yet.
</p>
) : (
<div class="space-y-4">
{service.internalNotes.map((note) => (
<div class="border-night-600 bg-night-800 rounded-md border p-4">
<input
type="checkbox"
class="peer/edit-note sr-only"
data-edit-note-checkbox
id={`edit-note-${note.id}`}
/>
<div class="flex items-start justify-between">
<div class="flex-grow space-y-1">
<div
data-note-content
class="prose text-day-200 prose-sm prose-invert max-w-none text-pretty"
>
<Markdown content={note.content} />
</div>
<div class="text-day-500 flex items-center gap-2 text-xs">
<TimeFormatted date={note.createdAt} hourPrecision />
{note.addedByUser && (
<span class="flex items-center gap-1">
by <UserBadge user={note.addedByUser} size="sm" />
</span>
)}
</div>
</div>
<div class="flex items-center gap-1">
<Button
as="label"
for={`edit-note-${note.id}`}
variant="faded"
size="sm"
icon="ri:edit-line"
iconOnly
label="Edit"
/>
<form method="POST" action={actions.admin.service.internalNote.delete} class="contents">
<input type="hidden" name="noteId" value={note.id} />
<Button
type="submit"
size="sm"
variant="faded"
icon="ri:delete-bin-line"
iconOnly
label="Delete"
/>
</form>
</div>
</div>
<form
method="POST"
action={actions.admin.service.internalNote.update}
data-note-edit-form
data-astro-reload
class="mt-4 hidden space-y-4 peer-checked/edit-note:block"
>
<input type="hidden" name="noteId" value={note.id} />
<InputTextArea
label="Note Content"
name="content"
value={note.content}
inputProps={{ class: 'bg-night-700' }}
/>
<InputSubmitButton label="Save" icon="ri:save-line" hideCancel />
</form>
</div>
))}
</div>
)
}
</FormSubSection>
<FormSubSection title="Add New Note">
<form method="POST" action={actions.admin.service.internalNote.add} class="space-y-4">
<input type="hidden" name="serviceId" value={service.id} />
<InputTextArea
label="Note Content"
name="content"
inputProps={{
required: true,
rows: 4,
placeholder: 'Add internal note about this service...',
}}
error={internalNoteInputErrors.content}
/>
<InputSubmitButton label="Add Note" icon="ri:add-line" hideCancel />
</form>
</FormSubSection>
</FormSection>
<FormSection title="Events">
<FormSubSection title="Existing Events">
{
@@ -898,7 +1025,7 @@ if (!service) return Astro.rewrite('/404')
<Tooltip text="Delete">
<form
method="POST"
action={actions.admin.service.deleteContactMethod}
action={actions.admin.service.contactMethod.delete}
class="contents"
>
<input type="hidden" name="id" value={method.id} />
@@ -919,7 +1046,7 @@ if (!service) return Astro.rewrite('/404')
<form
id={`edit-contact-method-${method.id}`}
method="POST"
action={actions.admin.service.updateContactMethod}
action={actions.admin.service.contactMethod.update}
class="border-night-500 bg-night-700 mt-3 hidden space-y-3 rounded-md border p-3"
>
<input type="hidden" name="id" value={method.id} />
@@ -954,7 +1081,7 @@ if (!service) return Astro.rewrite('/404')
</FormSubSection>
<FormSubSection title="Add New Contact Method">
<form method="POST" action={actions.admin.service.createContactMethod} class="space-y-2">
<form method="POST" action={actions.admin.service.contactMethod.add} class="space-y-2">
<input type="hidden" name="serviceId" value={service.id} />
<InputText label="Override Label" name="label" />

View File

@@ -111,6 +111,7 @@ const services = await Astro.locals.banners.try(
_count: {
select: {
verificationRequests: true,
internalNotes: true,
},
},
},
@@ -440,9 +441,18 @@ const truncate = (text: string, length: number) => {
</div>
</td>
<td class="px-4 py-3 text-center">
<span class="inline-flex items-center rounded-full bg-orange-900/30 px-2.5 py-0.5 text-xs text-orange-400">
{service._count.verificationRequests}
</span>
<div class="flex flex-col items-center justify-center gap-1">
<span class="inline-flex items-center rounded-full bg-orange-900/30 px-2.5 py-0.5 text-xs text-orange-400">
{service._count.verificationRequests}
</span>
{service._count.internalNotes > 0 && (
<span class="inline-flex items-center text-purple-400">
<Icon name="ri:sticky-note-line" class="mr-0.5 size-4" />
{service._count.internalNotes}
</span>
)}
</div>
</td>
<td class="px-4 py-3 text-center text-xs text-zinc-400">{service.formattedDate}</td>
<td class="px-4 py-3 text-right">

View File

@@ -350,13 +350,13 @@ const inputErrors = isInputError(result?.error) ? result.error.fields : {}
</div>
<div>
<label class="font-title mb-2 block text-sm text-green-500" for="referral">referral</label>
<label class="font-title mb-2 block text-sm text-green-500" for="referral">referral link path</label>
<input
transition:persist
type="text"
name="referral"
id="referral"
placeholder="Optional referral code/link"
placeholder="e.g. ?ref=123 or /ref/123"
class="font-title w-full rounded-md border border-green-500/30 bg-black/50 p-2 text-gray-300 placeholder-gray-500 focus:border-green-500 focus:ring-green-500"
/>
{
@@ -366,6 +366,26 @@ const inputErrors = isInputError(result?.error) ? result.error.fields : {}
}
</div>
<div>
<label for="internalNote" class="font-title mb-2 block text-sm text-green-500">internalNote</label>
<div class="space-y-2">
<textarea
transition:persist
name="internalNote"
id="internalNote"
rows={4}
placeholder="Markdown supported"
class="font-title w-full rounded-md border border-green-500/30 bg-black/50 p-2 text-gray-300 placeholder-gray-500 focus:border-green-500 focus:ring-green-500"
set:text=""
/>
</div>
{
inputErrors.internalNote && (
<p class="font-title mt-1 text-sm text-red-500">{inputErrors.internalNote.join(', ')}</p>
)
}
</div>
<button
type="submit"
class="font-title inline-flex justify-center rounded-md border border-green-500/30 bg-green-500/10 px-4 py-2 text-sm text-green-400 shadow-xs transition-colors duration-200 hover:bg-green-500/20 focus:ring-2 focus:ring-green-500 focus:ring-offset-2 focus:ring-offset-black focus:outline-hidden"

View File

@@ -1,5 +1,6 @@
---
import { Icon } from 'astro-icon/components'
import { Markdown } from 'astro-remote'
import { actions, isInputError } from 'astro:actions'
import BadgeSmall from '../../../components/BadgeSmall.astro'
@@ -304,8 +305,8 @@ if (!user) return Astro.rewrite('/404')
</div>
</div>
<div data-note-content>
<p class="text-day-200 wrap-anywhere whitespace-pre-wrap" set:text={note.content} />
<div data-note-content class="prose prose-sm text-day-200 prose-invert max-w-none text-pretty">
<Markdown content={note.content} />
</div>
<form