e8d5b16acc
- Add mobile viewport detection to ToolWorkshopPage - Implement mobile list view with MobileListView component - Implement mobile detail view with MobileDetailView component - Implement mobile edit view with MobileEditView component - Add MobileFAB for creating new tool types - Fix IconName type issues in mobile components - TypeScript check passes, build succeeds
96 lines
2.5 KiB
TypeScript
96 lines
2.5 KiB
TypeScript
import { Icon } from "./icon";
|
|
|
|
interface Field {
|
|
label: string;
|
|
value: string | number | boolean | null;
|
|
type?: "text" | "code" | "json" | "boolean";
|
|
}
|
|
|
|
interface MobileDetailViewProps {
|
|
title: string;
|
|
subtitle?: string;
|
|
fields: Field[];
|
|
onEdit: () => void;
|
|
onDelete: () => void;
|
|
onBack: () => void;
|
|
}
|
|
|
|
export const MobileDetailView: React.FC<MobileDetailViewProps> = ({
|
|
title,
|
|
subtitle,
|
|
fields,
|
|
onEdit,
|
|
onDelete,
|
|
onBack,
|
|
}) => {
|
|
const renderValue = (field: Field) => {
|
|
if (field.value === null || field.value === undefined) {
|
|
return <span className="text-muted">Not set</span>;
|
|
}
|
|
|
|
if (field.type === "boolean") {
|
|
return field.value ? (
|
|
<span className="badge badge-success">Yes</span>
|
|
) : (
|
|
<span className="badge badge-secondary">No</span>
|
|
);
|
|
}
|
|
|
|
if (field.type === "code" || field.type === "json") {
|
|
return (
|
|
<pre className="mobile-detail-code">
|
|
{typeof field.value === "string" ? field.value : JSON.stringify(field.value, null, 2)}
|
|
</pre>
|
|
);
|
|
}
|
|
|
|
return <span>{String(field.value)}</span>;
|
|
};
|
|
|
|
return (
|
|
<div className="mobile-detail-view">
|
|
<header className="mobile-detail-header">
|
|
<button
|
|
className="mobile-detail-back"
|
|
onClick={onBack}
|
|
type="button"
|
|
aria-label="Go back"
|
|
>
|
|
<Icon name="arrow-left" size="md" />
|
|
</button>
|
|
<div className="mobile-detail-header-content">
|
|
<h1 className="mobile-detail-title">{title}</h1>
|
|
{subtitle && <p className="mobile-detail-subtitle">{subtitle}</p>}
|
|
</div>
|
|
<div className="mobile-detail-actions">
|
|
<button
|
|
className="mobile-detail-action"
|
|
onClick={onEdit}
|
|
type="button"
|
|
aria-label="Edit"
|
|
>
|
|
<Icon name="edit" size="sm" />
|
|
</button>
|
|
<button
|
|
className="mobile-detail-action mobile-detail-action-danger"
|
|
onClick={onDelete}
|
|
type="button"
|
|
aria-label="Delete"
|
|
>
|
|
<Icon name="delete" size="sm" />
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div className="mobile-detail-fields">
|
|
{fields.map((field, index) => (
|
|
<div key={index} className="mobile-detail-field">
|
|
<label className="mobile-detail-field-label">{field.label}</label>
|
|
<div className="mobile-detail-field-value">{renderValue(field)}</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|