upd: swagger docs, api key auth, bug fixes

- tambah Elysia Swagger di /docs dengan deskripsi lengkap semua endpoint
- tambah API key auth (X-API-Key) untuk klien eksternal di POST /api/bugs
- tambah normalisasi BugSource: SYSTEM/USER untuk eksternal, QC/SYSTEM/USER untuk dashboard
- perbaiki source schema jadi optional string agar tidak reject nilai unknown dari klien lama
- hapus field status dari form create bug (selalu OPEN)
- perbaiki typo desa_plus → appId di apps.$appId.errors.tsx
- tambah toggle hide/show stack trace di bug-reports.tsx dan apps.$appId.errors.tsx
- perbaiki grafik desa (width(-1)/height(-1)) dengan minWidth: 0 pada grid item
- perbaiki error &[data-active] inline style di DashboardLayout → pindah ke CSS class
- update CLAUDE.md dengan arsitektur lengkap

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-21 17:30:07 +08:00
parent cd295abf2c
commit d09a702d64
13 changed files with 545 additions and 202 deletions

View File

@@ -80,7 +80,6 @@ function AppErrorsPage() {
const [createForm, setCreateForm] = useState({
description: '',
app: appId,
status: 'OPEN',
source: 'USER',
affectedVersion: '',
device: '',
@@ -212,8 +211,7 @@ function AppErrorsPage() {
close()
setCreateForm({
description: '',
app: 'desa_plus',
status: 'OPEN',
app: appId,
source: 'USER',
affectedVersion: '',
device: '',
@@ -368,25 +366,13 @@ function AppErrorsPage() {
/>
</SimpleGrid>
<SimpleGrid cols={2}>
<TextInput
label="Version"
placeholder="e.g. 2.4.1"
required
value={createForm.affectedVersion}
onChange={(e) => setCreateForm({ ...createForm, affectedVersion: e.target.value })}
/>
<Select
label="Initial Status"
data={[
{ value: 'OPEN', label: 'Open' },
{ value: 'ON_HOLD', label: 'On Hold' },
{ value: 'IN_PROGRESS', label: 'In Progress' },
]}
value={createForm.status}
onChange={(val) => setCreateForm({ ...createForm, status: val as any })}
/>
</SimpleGrid>
<TextInput
label="Version"
placeholder="e.g. 2.4.1"
required
value={createForm.affectedVersion}
onChange={(e) => setCreateForm({ ...createForm, affectedVersion: e.target.value })}
/>
<SimpleGrid cols={2}>
<TextInput
@@ -525,7 +511,7 @@ function AppErrorsPage() {
</Group>
<Group gap="md">
<Text size="xs" c="dimmed">
{new Date(bug.createdAt).toLocaleString()} {bug.app?.toUpperCase()} v{bug.affectedVersion}
{new Date(bug.createdAt).toLocaleString()} {bug.appId?.toUpperCase()} v{bug.affectedVersion}
</Text>
</Group>
</Box>

View File

@@ -73,7 +73,14 @@ function ActivityChart({ villageId }: { villageId: string }) {
yearly: 'Yearly',
}
const data = response?.data || []
const rawData: any[] = Array.isArray(response?.data) ? response.data : []
// Normalize: map any field names from external API → { label, activity }
const data = rawData.map((item) => {
const label = item.label
const activity = item.aktivitas
return { label: String(label), activity: Number(activity) }
})
return (
<Paper withBorder radius="xl" p="lg">
@@ -430,7 +437,9 @@ function VillageDetailPage() {
}}
>
{/* Left (3/4): Activity Chart */}
<ActivityChart villageId={villageId} />
<Box style={{ minWidth: 0 }}>
<ActivityChart villageId={villageId} />
</Box>
{/* Right (1/4): Informasi Sistem */}
<Paper withBorder radius="xl" p="lg">
@@ -444,7 +453,7 @@ function VillageDetailPage() {
{[
{ label: 'Date Created', value: village.createdAt },
{ label: 'Created By', value: '-' },
{ label: 'Last Updated', value: '-' },
{ label: 'Last Updated', value: village.updatedAt },
].map((item, idx, arr) => (
<Group
key={item.label}

View File

@@ -55,10 +55,14 @@ function ListErrorsPage() {
const [status, setStatus] = useState('all')
const [showLogs, setShowLogs] = useState<Record<string, boolean>>({})
const [showStackTrace, setShowStackTrace] = useState<Record<string, boolean>>({})
const toggleLogs = (bugId: string) => {
setShowLogs((prev) => ({ ...prev, [bugId]: !prev[bugId] }))
}
const toggleStackTrace = (bugId: string) => {
setShowStackTrace((prev) => ({ ...prev, [bugId]: !prev[bugId] }))
}
const { data, isLoading, refetch } = useQuery({
queryKey: ['bugs', { page, search, app, status }],
@@ -78,7 +82,6 @@ function ListErrorsPage() {
const [createForm, setCreateForm] = useState({
description: '',
app: 'desa-plus',
status: 'OPEN',
source: 'USER',
affectedVersion: '',
device: '',
@@ -211,7 +214,6 @@ function ListErrorsPage() {
setCreateForm({
description: '',
app: 'desa-plus',
status: 'OPEN',
source: 'USER',
affectedVersion: '',
device: '',
@@ -377,25 +379,13 @@ function ListErrorsPage() {
/>
</SimpleGrid>
<SimpleGrid cols={2}>
<TextInput
label="Version"
placeholder="e.g. 2.4.1"
required
value={createForm.affectedVersion}
onChange={(e) => setCreateForm({ ...createForm, affectedVersion: e.target.value })}
/>
<Select
label="Initial Status"
data={[
{ value: 'OPEN', label: 'Open' },
{ value: 'ON_HOLD', label: 'On Hold' },
{ value: 'IN_PROGRESS', label: 'In Progress' },
]}
value={createForm.status}
onChange={(val) => setCreateForm({ ...createForm, status: val as any })}
/>
</SimpleGrid>
<TextInput
label="Version"
placeholder="e.g. 2.4.1"
required
value={createForm.affectedVersion}
onChange={(e) => setCreateForm({ ...createForm, affectedVersion: e.target.value })}
/>
<SimpleGrid cols={2}>
<TextInput
@@ -545,7 +535,7 @@ function ListErrorsPage() {
</Group>
<Group gap="md">
<Text size="xs" c="dimmed">
{new Date(bug.createdAt).toLocaleString()} {bug.app?.toUpperCase()} v{bug.affectedVersion}
{new Date(bug.createdAt).toLocaleString()} {bug.appId?.toUpperCase()} v{bug.affectedVersion}
</Text>
</Group>
</Box>
@@ -598,19 +588,31 @@ function ListErrorsPage() {
{/* Stack Trace */}
{bug.stackTrace && (
<Box>
<Text size="xs" fw={700} c="dimmed" mb={4}>STACK TRACE</Text>
<Code
block
color="red"
style={{
fontFamily: 'monospace',
whiteSpace: 'pre-wrap',
fontSize: '11px',
border: '1px solid var(--mantine-color-default-border)',
}}
>
{bug.stackTrace}
</Code>
<Group justify="space-between" mb={showStackTrace[bug.id] ? 8 : 0}>
<Text size="xs" fw={700} c="dimmed">STACK TRACE</Text>
<Button
variant="subtle"
size="compact-xs"
color="gray"
onClick={() => toggleStackTrace(bug.id)}
>
{showStackTrace[bug.id] ? 'Hide' : 'Show'}
</Button>
</Group>
<Collapse in={showStackTrace[bug.id]}>
<Code
block
color="red"
style={{
fontFamily: 'monospace',
whiteSpace: 'pre-wrap',
fontSize: '11px',
border: '1px solid var(--mantine-color-default-border)',
}}
>
{bug.stackTrace}
</Code>
</Collapse>
</Box>
)}

View File

@@ -367,9 +367,9 @@ function UsersPage() {
))}
</List>
<Button fullWidth variant="light" color={role.color} mt="md" radius="md">
{/* <Button fullWidth variant="light" color={role.color} mt="md" radius="md">
Edit Permissions
</Button>
</Button> */}
</Stack>
</Card>
))}