feat(frontend): implement unified design system with 17 reusable template components, skeleton loading states, and golden-ratio-based layouts
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useState, useMemo } from 'react';
|
||||
import {
|
||||
Box, Typography, Paper, Chip, IconButton, Button, Dialog, DialogTitle,
|
||||
DialogContent, DialogActions, TextField, MenuItem, Select, FormControl,
|
||||
Box, Typography, Paper, Chip, IconButton, Button,
|
||||
TextField, MenuItem, Select, FormControl,
|
||||
InputLabel, Divider, CircularProgress, Autocomplete, Grid, Card, CardContent,
|
||||
List, ListItem, ListItemIcon, ListItemText, ListItemSecondaryAction,
|
||||
} from '@mui/material';
|
||||
@@ -20,6 +20,7 @@ import { usePermissionContext } from '../contexts/PermissionContext';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { issuesApi } from '../services/issues';
|
||||
import type { Issue, IssueComment, UpdateIssuePayload, AssignableMember, IssueStatusDef, IssuePriorityDef, IssueHistorie, IssueDatei } from '../types/issue.types';
|
||||
import { ConfirmDialog, FormDialog, PageHeader } from '../components/templates';
|
||||
|
||||
// ── Helpers (copied from Issues.tsx) ──
|
||||
|
||||
@@ -260,21 +261,16 @@ export default function IssueDetail() {
|
||||
return (
|
||||
<DashboardLayout>
|
||||
<Box sx={{ p: 3 }}>
|
||||
{/* Header */}
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 3 }}>
|
||||
<IconButton onClick={() => navigate('/issues')}>
|
||||
<ArrowBack />
|
||||
</IconButton>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<Typography variant="h5">
|
||||
{formatIssueId(issue)} — {issue.titel}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Chip
|
||||
label={getStatusLabel(statuses, issue.status)}
|
||||
color={getStatusColor(statuses, issue.status)}
|
||||
/>
|
||||
</Box>
|
||||
<PageHeader
|
||||
title={`${formatIssueId(issue)} — ${issue.titel}`}
|
||||
backTo="/issues"
|
||||
actions={
|
||||
<Chip
|
||||
label={getStatusLabel(statuses, issue.status)}
|
||||
color={getStatusColor(statuses, issue.status)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Info cards */}
|
||||
<Grid container spacing={2} sx={{ mb: 3 }}>
|
||||
@@ -559,50 +555,38 @@ export default function IssueDetail() {
|
||||
</Box>
|
||||
|
||||
{/* Reopen Dialog */}
|
||||
<Dialog open={reopenOpen} onClose={() => setReopenOpen(false)} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>Issue wiedereröffnen</DialogTitle>
|
||||
<DialogContent sx={{ display: 'flex', flexDirection: 'column', gap: 2, pt: '20px !important' }}>
|
||||
<TextField
|
||||
label="Kommentar (Pflicht)"
|
||||
required
|
||||
multiline
|
||||
rows={3}
|
||||
fullWidth
|
||||
value={reopenComment}
|
||||
onChange={(e) => setReopenComment(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setReopenOpen(false)}>Abbrechen</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
disabled={!reopenComment.trim() || updateMut.isPending}
|
||||
onClick={handleReopen}
|
||||
>
|
||||
Wiedereröffnen
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<FormDialog
|
||||
open={reopenOpen}
|
||||
onClose={() => setReopenOpen(false)}
|
||||
onSubmit={handleReopen}
|
||||
title="Issue wiedereröffnen"
|
||||
submitLabel="Wiedereröffnen"
|
||||
isSubmitting={updateMut.isPending}
|
||||
maxWidth="sm"
|
||||
>
|
||||
<TextField
|
||||
label="Kommentar (Pflicht)"
|
||||
required
|
||||
multiline
|
||||
rows={3}
|
||||
fullWidth
|
||||
value={reopenComment}
|
||||
onChange={(e) => setReopenComment(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
</FormDialog>
|
||||
|
||||
{/* Delete Confirmation Dialog */}
|
||||
<Dialog open={deleteOpen} onClose={() => setDeleteOpen(false)} maxWidth="xs" fullWidth>
|
||||
<DialogTitle>Issue löschen</DialogTitle>
|
||||
<DialogContent>
|
||||
<Typography>Soll dieses Issue wirklich gelöscht werden?</Typography>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={() => setDeleteOpen(false)}>Abbrechen</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
disabled={deleteMut.isPending}
|
||||
onClick={() => deleteMut.mutate()}
|
||||
>
|
||||
Löschen
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
<ConfirmDialog
|
||||
open={deleteOpen}
|
||||
onClose={() => setDeleteOpen(false)}
|
||||
onConfirm={() => deleteMut.mutate()}
|
||||
title="Issue löschen"
|
||||
message="Soll dieses Issue wirklich gelöscht werden?"
|
||||
confirmLabel="Löschen"
|
||||
confirmColor="error"
|
||||
isLoading={deleteMut.isPending}
|
||||
/>
|
||||
</DashboardLayout>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user