diff --git a/backend/resources/js/Pages/admin/customers/Create.jsx b/backend/resources/js/Pages/admin/customers/Create.jsx
index 39b6eb35b..1275df6cd 100644
--- a/backend/resources/js/Pages/admin/customers/Create.jsx
+++ b/backend/resources/js/Pages/admin/customers/Create.jsx
@@ -1,7 +1,7 @@
import { Head } from '@inertiajs/react';
import AppLayout from '../../../layouts/AppLayout';
import ResourceForm from '../../../components/ResourceForm';
-import { customerFields, CUSTOMER_INITIAL } from './fields';
+import { customerFields, CUSTOMER_INITIAL, customerInitial } from './fields';
import { __ } from '../../../lib/i18n';
export default function CustomerCreate({ basePath }) {
@@ -13,7 +13,7 @@ export default function CustomerCreate({ basePath }) {
action={basePath}
method="post"
fields={customerFields()}
- initial={CUSTOMER_INITIAL}
+ initial={customerInitial(null)}
submitLabel={__('Create')}
cancelHref={basePath}
/>
diff --git a/backend/resources/js/Pages/admin/customers/Edit.jsx b/backend/resources/js/Pages/admin/customers/Edit.jsx
index 657d5db0e..a75ceb8f6 100644
--- a/backend/resources/js/Pages/admin/customers/Edit.jsx
+++ b/backend/resources/js/Pages/admin/customers/Edit.jsx
@@ -1,7 +1,7 @@
import { Head } from '@inertiajs/react';
import AppLayout from '../../../layouts/AppLayout';
import ResourceForm from '../../../components/ResourceForm';
-import { customerFields } from './fields';
+import { customerFields, customerInitial } from './fields';
import { __ } from '../../../lib/i18n';
export default function CustomerEdit({ customer, basePath }) {
@@ -13,14 +13,7 @@ export default function CustomerEdit({ customer, basePath }) {
action={`${basePath}/${customer.id}`}
method="put"
fields={customerFields()}
- initial={{
- name: customer.name ?? '',
- code: customer.code ?? '',
- tier: customer.tier ?? 'bronze',
- payment_score: customer.payment_score ?? 0,
- notes: customer.notes ?? '',
- is_active: !!customer.is_active,
- }}
+ initial={customerInitial(customer)}
submitLabel={__('Save Changes')}
cancelHref={basePath}
/>
diff --git a/backend/resources/js/Pages/admin/customers/Index.jsx b/backend/resources/js/Pages/admin/customers/Index.jsx
index e731d3811..ea5688ab4 100644
--- a/backend/resources/js/Pages/admin/customers/Index.jsx
+++ b/backend/resources/js/Pages/admin/customers/Index.jsx
@@ -1,16 +1,14 @@
-import { useState } from 'react';
import { Head, router, usePage } from '@inertiajs/react';
-import { Modal } from '@openmes/ui';
import AppLayout from '../../../layouts/AppLayout';
import ResourceTable, { ActiveBadge } from '../../../components/ResourceTable';
-import ResourceForm from '../../../components/ResourceForm';
-import { TIER_BADGE_STYLES, tierLabel, customerFields, CUSTOMER_INITIAL } from './fields';
+import ResourceFormDrawer, { useResourceDrawer } from '../../../components/ResourceFormDrawer';
+import { TIER_BADGE_STYLES, tierLabel, customerFields, customerInitial } from './fields';
import { __ } from '../../../lib/i18n';
export default function CustomersIndex() {
const { counts = {}, basePath } = usePage().props;
- const [creating, setCreating] = useState(false);
- const [formKey, setFormKey] = useState(0);
+
+ const drawer = useResourceDrawer();
const columns = [
{ key: 'name', label: __('Name'), className: 'font-medium text-om-ink', filter: 'text' },
@@ -33,7 +31,9 @@ export default function CustomersIndex() {
];
const actions = (r) => [
- { label: __('Edit'), href: `${basePath}/${r.id}/edit` },
+ // The row is the record: `customers` syncs every column the form needs,
+ // so the drawer opens filled in without a round-trip.
+ { label: __('Edit'), onClick: () => drawer.edit(r) },
{
label: r.is_active ? __('Deactivate') : __('Activate'),
onClick: () => router.post(`${basePath}/${r.id}/toggle-active`, {}, { preserveScroll: true }),
@@ -56,7 +56,7 @@ export default function CustomersIndex() {
shape="customers"
title={__('Customers')}
createHref={`${basePath}/create`}
- onCreate={() => setCreating(true)}
+ onCreate={drawer.create}
createLabel={__('New Customer')}
columns={columns}
orderBy="name"
@@ -64,42 +64,13 @@ export default function CustomersIndex() {
emptyText={__('No customers yet.')}
/>
-
setCreating(false)}
- title={__('New Customer')}
- closeLabel={__('Close')}
- className="max-w-[720px]"
- // A misclick on the scrim shouldn't cost a half-filled customer.
- keepMounted
- >
- {/* The same field config the create page renders, so a field added
- to `customerFields()` shows up in both. `stay: 1` makes the
- controller answer with back(), keeping this list's filters and
- paging while the new row live-syncs in. */}
- {/* `keepMounted` holds the form's state, which is the point when
- you close by accident. Bumping the key remounts the form for the
- two cases that are not accidents — a finished create, and an
- explicit Cancel — so neither lingers into the next one. */}
- {
- setCreating(false);
- setFormKey((k) => k + 1);
- }}
- onSuccess={() => {
- setCreating(false);
- setFormKey((k) => k + 1);
- }}
- />
-
+
>
);
}
diff --git a/backend/resources/js/Pages/admin/customers/fields.js b/backend/resources/js/Pages/admin/customers/fields.js
index 95a860aa0..93eb7c730 100644
--- a/backend/resources/js/Pages/admin/customers/fields.js
+++ b/backend/resources/js/Pages/admin/customers/fields.js
@@ -49,3 +49,25 @@ export function customerFields() {
{ name: 'is_active', label: __('Active'), type: 'checkbox' },
];
}
+
+/**
+ * A record as form values, and with no record an empty form.
+ *
+ * One definition shared by Create.jsx, Edit.jsx and the list's create/edit
+ * drawer, so the three can't drift on what a blank field is or how a stored
+ * value is coerced for the input that shows it.
+ */
+export function customerInitial(record) {
+ if (!record) {
+ return { ...CUSTOMER_INITIAL };
+ }
+
+ return {
+ name: record.name ?? '',
+ code: record.code ?? '',
+ tier: record.tier ?? 'bronze',
+ payment_score: record.payment_score ?? 0,
+ notes: record.notes ?? '',
+ is_active: !!record.is_active,
+ };
+}
diff --git a/backend/resources/js/Pages/admin/divisions/Create.jsx b/backend/resources/js/Pages/admin/divisions/Create.jsx
index e84efbf78..904e49ff1 100644
--- a/backend/resources/js/Pages/admin/divisions/Create.jsx
+++ b/backend/resources/js/Pages/admin/divisions/Create.jsx
@@ -1,7 +1,7 @@
import { Head, usePage } from '@inertiajs/react';
import AppLayout from '../../../layouts/AppLayout';
import ResourceForm from '../../../components/ResourceForm';
-import { divisionFields } from './fields';
+import { divisionFields, divisionInitial } from './fields';
import { __ } from '../../../lib/i18n';
export default function DivisionCreate() {
@@ -15,7 +15,7 @@ export default function DivisionCreate() {
action="/admin/divisions"
method="post"
fields={divisionFields(factories)}
- initial={{ factory_id: '', code: '', name: '', description: '', is_active: true }}
+ initial={divisionInitial(null)}
submitLabel={__('Create')}
cancelHref="/admin/divisions"
/>
diff --git a/backend/resources/js/Pages/admin/divisions/Edit.jsx b/backend/resources/js/Pages/admin/divisions/Edit.jsx
index 1eebc6a4f..83a8ecca4 100644
--- a/backend/resources/js/Pages/admin/divisions/Edit.jsx
+++ b/backend/resources/js/Pages/admin/divisions/Edit.jsx
@@ -1,7 +1,7 @@
import { Head, usePage } from '@inertiajs/react';
import AppLayout from '../../../layouts/AppLayout';
import ResourceForm from '../../../components/ResourceForm';
-import { divisionFields } from './fields';
+import { divisionFields, divisionInitial } from './fields';
import { __ } from '../../../lib/i18n';
export default function DivisionEdit() {
@@ -15,13 +15,7 @@ export default function DivisionEdit() {
action={`/admin/divisions/${division.id}`}
method="put"
fields={divisionFields(factories)}
- initial={{
- factory_id: division.factory_id != null ? String(division.factory_id) : '',
- code: division.code ?? '',
- name: division.name ?? '',
- description: division.description ?? '',
- is_active: !!division.is_active,
- }}
+ initial={divisionInitial(division)}
submitLabel={__('Save Changes')}
cancelHref="/admin/divisions"
/>
diff --git a/backend/resources/js/Pages/admin/divisions/Index.jsx b/backend/resources/js/Pages/admin/divisions/Index.jsx
index 9c1312916..038911561 100644
--- a/backend/resources/js/Pages/admin/divisions/Index.jsx
+++ b/backend/resources/js/Pages/admin/divisions/Index.jsx
@@ -1,10 +1,14 @@
import { Head, router, usePage } from '@inertiajs/react';
import AppLayout from '../../../layouts/AppLayout';
import ResourceTable, { ActiveBadge } from '../../../components/ResourceTable';
+import ResourceFormDrawer, { useResourceDrawer } from '../../../components/ResourceFormDrawer';
import { __ } from '../../../lib/i18n';
+import { divisionFields, divisionInitial } from './fields';
export default function DivisionsIndex() {
- const { counts = {}, factoryNames = {} } = usePage().props;
+ const { counts = {}, factoryNames = {}, factories } = usePage().props;
+
+ const drawer = useResourceDrawer();
const columns = [
{ key: 'code', label: __('Code'), className: 'font-mono text-om-muted' },
@@ -15,7 +19,7 @@ export default function DivisionsIndex() {
];
const actions = (r) => [
- { label: __('Edit'), icon: 'edit', href: `/admin/divisions/${r.id}/edit` },
+ { label: __('Edit'), icon: 'edit', onClick: () => drawer.edit(r) },
{
label: r.is_active ? __('Deactivate') : __('Activate'),
icon: r.is_active ? 'deactivate' : 'activate',
@@ -40,12 +44,23 @@ export default function DivisionsIndex() {
shape="divisions"
title={__('Divisions')}
createHref="/admin/divisions/create"
+ onCreate={drawer.create}
createLabel={__('New Division')}
columns={columns}
orderBy="name"
actions={actions}
emptyText={__('No divisions yet.')}
/>
+
+
>
);
}
diff --git a/backend/resources/js/Pages/admin/divisions/fields.js b/backend/resources/js/Pages/admin/divisions/fields.js
index bf6ed48e9..41213310f 100644
--- a/backend/resources/js/Pages/admin/divisions/fields.js
+++ b/backend/resources/js/Pages/admin/divisions/fields.js
@@ -17,3 +17,24 @@ export function divisionFields(factories) {
{ name: 'is_active', label: __('Active'), type: 'checkbox' },
];
}
+
+/**
+ * A record as form values, and with no record an empty form.
+ *
+ * One definition shared by Create.jsx, Edit.jsx and the list's create/edit
+ * drawer, so the three can't drift on what a blank field is or how a stored
+ * value is coerced for the input that shows it.
+ */
+export function divisionInitial(record) {
+ if (!record) {
+ return { factory_id: '', code: '', name: '', description: '', is_active: true };
+ }
+
+ return {
+ factory_id: record.factory_id != null ? String(record.factory_id) : '',
+ code: record.code ?? '',
+ name: record.name ?? '',
+ description: record.description ?? '',
+ is_active: !!record.is_active,
+ };
+}
diff --git a/backend/resources/js/Pages/admin/factories/Create.jsx b/backend/resources/js/Pages/admin/factories/Create.jsx
index 3fabb9cdb..9f8ce6eef 100644
--- a/backend/resources/js/Pages/admin/factories/Create.jsx
+++ b/backend/resources/js/Pages/admin/factories/Create.jsx
@@ -1,7 +1,7 @@
import { Head } from '@inertiajs/react';
import AppLayout from '../../../layouts/AppLayout';
import ResourceForm from '../../../components/ResourceForm';
-import { FACTORY_FIELDS } from './fields';
+import { FACTORY_FIELDS, factoryInitial } from './fields';
import { __ } from '../../../lib/i18n';
export default function FactoryCreate() {
@@ -13,7 +13,7 @@ export default function FactoryCreate() {
action="/admin/factories"
method="post"
fields={FACTORY_FIELDS}
- initial={{ code: '', name: '', description: '', is_active: true }}
+ initial={factoryInitial(null)}
submitLabel={__('Create')}
cancelHref="/admin/factories"
/>
diff --git a/backend/resources/js/Pages/admin/factories/Edit.jsx b/backend/resources/js/Pages/admin/factories/Edit.jsx
index 8009e57ff..ffc2654d2 100644
--- a/backend/resources/js/Pages/admin/factories/Edit.jsx
+++ b/backend/resources/js/Pages/admin/factories/Edit.jsx
@@ -1,7 +1,7 @@
import { Head } from '@inertiajs/react';
import AppLayout from '../../../layouts/AppLayout';
import ResourceForm from '../../../components/ResourceForm';
-import { FACTORY_FIELDS } from './fields';
+import { FACTORY_FIELDS, factoryInitial } from './fields';
import { __ } from '../../../lib/i18n';
export default function FactoryEdit({ factory }) {
@@ -13,12 +13,7 @@ export default function FactoryEdit({ factory }) {
action={`/admin/factories/${factory.id}`}
method="put"
fields={FACTORY_FIELDS}
- initial={{
- code: factory.code ?? '',
- name: factory.name ?? '',
- description: factory.description ?? '',
- is_active: !!factory.is_active,
- }}
+ initial={factoryInitial(factory)}
submitLabel={__('Save Changes')}
cancelHref="/admin/factories"
/>
diff --git a/backend/resources/js/Pages/admin/factories/Index.jsx b/backend/resources/js/Pages/admin/factories/Index.jsx
index fcb107fbf..4531a0742 100644
--- a/backend/resources/js/Pages/admin/factories/Index.jsx
+++ b/backend/resources/js/Pages/admin/factories/Index.jsx
@@ -1,20 +1,24 @@
import { Head, router, usePage } from '@inertiajs/react';
import AppLayout from '../../../layouts/AppLayout';
import ResourceTable, { ActiveBadge } from '../../../components/ResourceTable';
+import ResourceFormDrawer, { useResourceDrawer } from '../../../components/ResourceFormDrawer';
import { __ } from '../../../lib/i18n';
+import { FACTORY_FIELDS, factoryInitial } from './fields';
export default function FactoriesIndex() {
+ const drawer = useResourceDrawer();
+
const { counts = {} } = usePage().props;
const columns = [
{ key: 'code', label: __('Code'), className: 'font-mono text-om-muted' },
- { key: 'name', label: __('Name'), className: 'font-medium text-om-ink', filter: 'text' },
+ { key: 'name', label: __('Name'), className: 'font-medium text-om-ink', filter: 'text', link: true },
{ key: 'divisions', label: __('Divisions'), value: (r) => counts[r.id] ?? 0, render: (r) => counts[r.id] ?? 0 },
{ key: 'is_active', label: __('Status'), value: (r) => __(r.is_active ? 'Active' : 'Inactive'), render: (r) =>
},
];
const actions = (r) => [
- { label: __('Edit'), icon: 'edit', href: `/admin/factories/${r.id}/edit` },
+ { label: __('Edit'), icon: 'edit', onClick: () => drawer.edit(r) },
{
label: r.is_active ? __('Deactivate') : __('Activate'),
icon: r.is_active ? 'deactivate' : 'activate',
@@ -40,12 +44,21 @@ export default function FactoriesIndex() {
detailHref={(r) => `/admin/factories/${r.id}`}
title={__('Factories')}
createHref="/admin/factories/create"
+ onCreate={drawer.create}
createLabel={__('New Factory')}
columns={columns}
orderBy="name"
actions={actions}
emptyText={__('No factories yet.')}
/>
+
+
>
);
}
diff --git a/backend/resources/js/Pages/admin/factories/fields.js b/backend/resources/js/Pages/admin/factories/fields.js
index 3f443a0fc..94d4b8722 100644
--- a/backend/resources/js/Pages/admin/factories/fields.js
+++ b/backend/resources/js/Pages/admin/factories/fields.js
@@ -6,3 +6,23 @@ export const FACTORY_FIELDS = [
{ name: 'description', label: __('Description'), type: 'textarea' },
{ name: 'is_active', label: __('Active'), type: 'checkbox' },
];
+
+/**
+ * A record as form values, and with no record an empty form.
+ *
+ * One definition shared by Create.jsx, Edit.jsx and the list's create/edit
+ * drawer, so the three can't drift on what a blank field is or how a stored
+ * value is coerced for the input that shows it.
+ */
+export function factoryInitial(record) {
+ if (!record) {
+ return { code: '', name: '', description: '', is_active: true };
+ }
+
+ return {
+ code: record.code ?? '',
+ name: record.name ?? '',
+ description: record.description ?? '',
+ is_active: !!record.is_active,
+ };
+}
diff --git a/backend/resources/js/Pages/admin/inspection-plans/Form.jsx b/backend/resources/js/Pages/admin/inspection-plans/Form.jsx
index ad3e1e850..98dc554cf 100644
--- a/backend/resources/js/Pages/admin/inspection-plans/Form.jsx
+++ b/backend/resources/js/Pages/admin/inspection-plans/Form.jsx
@@ -30,18 +30,19 @@ export default function InspectionPlanForm({ form, materials, materialTypes, sub
return (