feat: use FileInput DS component for file uploads, fix certs volume perms
All checks were successful
CI / build (push) Successful in 1m24s
CI / docker (push) Successful in 1m12s

- Replace inline FileField and native <input type="file"> with
  FileInput from @cameleer/design-system (drag-and-drop, icons, clear)
- Update CertificatesPage and SsoPage to use FileInput + FormField
- Fix /certs volume permissions (chmod 775) so cameleer user can write

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-11 08:04:47 +02:00
parent 4fdf171912
commit 875b07fb3a
5 changed files with 39 additions and 71 deletions

View File

@@ -1,9 +1,9 @@
import { useState, useRef } from 'react';
import {
Alert, AlertDialog, Badge, Button, Card, DataTable,
EmptyState, FormField, Input, Spinner, useToast,
EmptyState, FileInput, FormField, Input, Spinner, useToast,
} from '@cameleer/design-system';
import type { Column } from '@cameleer/design-system';
import type { Column, FileInputHandle } from '@cameleer/design-system';
import { Shield, Plus, Trash2, FlaskConical, Upload, ShieldCheck } from 'lucide-react';
import {
useSsoConnectors, useCreateSsoConnector, useDeleteSsoConnector, useTestSsoConnector,
@@ -331,12 +331,12 @@ function CaCertificatesSection() {
const deleteMutation = useDeleteCaCert();
const { toast } = useToast();
const certInputRef = useRef<HTMLInputElement>(null);
const certRef = useRef<FileInputHandle>(null);
const [label, setLabel] = useState('');
const [deleteTarget, setDeleteTarget] = useState<CaCertResponse | null>(null);
async function handleUpload() {
const file = certInputRef.current?.files?.[0];
const file = certRef.current?.file;
if (!file) {
toast({ title: 'Select a CA certificate file', variant: 'error' });
return;
@@ -348,7 +348,7 @@ function CaCertificatesSection() {
try {
await stageMutation.mutateAsync(formData);
toast({ title: 'CA certificate staged', variant: 'success' });
if (certInputRef.current) certInputRef.current.value = '';
certRef.current?.clear();
setLabel('');
} catch (err) {
toast({ title: 'Upload failed', description: String(err), variant: 'error' });
@@ -409,21 +409,9 @@ function CaCertificatesSection() {
onChange={(e) => setLabel(e.target.value)}
/>
</div>
<div>
<label style={{ fontSize: 13, color: 'var(--text-secondary)', display: 'block', marginBottom: 4 }}>
CA Certificate (PEM) *
</label>
<input
ref={certInputRef}
type="file"
accept=".pem,.crt,.cer"
style={{
fontSize: 13, color: 'var(--text-primary)',
background: 'var(--bg-inset)', border: '1px solid var(--border)',
borderRadius: 6, padding: '6px 8px', width: '100%',
}}
/>
</div>
<FormField label="CA Certificate (PEM) *">
<FileInput ref={certRef} accept=".pem,.crt,.cer" icon={<ShieldCheck size={16} />} />
</FormField>
<Button variant="primary" onClick={handleUpload} loading={stageMutation.isPending}>
<Upload size={14} style={{ marginRight: 6 }} />
Stage Certificate