admin-panel: add shared modal/header/icon/delete-handler components
New src/components/common/: - BaseFormModal.vue — the standard shell every converted add/edit modal will use: header=title, footer=buttons, body=form. Replaces the ad-hoc 60%/70%/80% <a-modal> widths used across the codebase with 4 named sizes. - ListPageHeader.vue — the add button that goes above every list page, gated on the same rid the create route used (modelled on the one place this already existed, ListUser.vue's add-user button). New src/components/icons.ts: the legacy-Feather-name -> ant-icon map, extracted out of TooltipBtn.vue so the sidebar can resolve the same names through the same map instead of a second copy. New src/composables/useDeleteHandler.ts: the delete-then-refetch-then-toast handler that was copy-pasted verbatim in 6 list views (city/cityList.vue, user/ListUser.vue, apps/category/List.vue, finical/list.vue, apps/product/ProductList.vue, manage/Const.vue). Preserves each site's exact success-message wording and message.success/info choice, and ProductList's irregular POST-based delete endpoint. Verified: pnpm build passes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
f0de478940
commit
02d92fc465
@@ -0,0 +1,51 @@
|
||||
// Shared "delete a row, refresh the list, show the server's refusal reason"
|
||||
// handler. This exact try/catch body — api.delete, then either a success
|
||||
// toast + refetch, or Modal.error with the server's message — was copy-pasted
|
||||
// verbatim in 6 list views (city/cityList.vue, user/ListUser.vue,
|
||||
// apps/category/List.vue, finical/list.vue, apps/product/ProductList.vue,
|
||||
// manage/Const.vue). Centralised here as a composable rather than a component
|
||||
// since it is pure behaviour with no template.
|
||||
import { Modal, message } from 'ant-design-vue';
|
||||
import api from '../api/client';
|
||||
|
||||
export function useDeleteHandler(options: {
|
||||
/** Called after a confirmed, successful delete — typically `table.value?.fetch()`. */
|
||||
onSuccess: () => void | Promise<void>;
|
||||
successMessage?: string;
|
||||
/** Some call sites used message.info rather than message.success; preserved
|
||||
* per-site rather than silently unified. */
|
||||
successType?: 'success' | 'info';
|
||||
/** Shown when the server didn't return its own message. */
|
||||
fallbackErrorMessage?: string;
|
||||
}) {
|
||||
const {
|
||||
onSuccess,
|
||||
successMessage = 'با موفقیت حذف شد',
|
||||
successType = 'success',
|
||||
fallbackErrorMessage = 'خطا در حذف',
|
||||
} = options;
|
||||
|
||||
async function remove(url: string, opts?: { method?: 'delete' | 'post' }) {
|
||||
try {
|
||||
// ProductList's delete is a legacy irregular endpoint (POST, not
|
||||
// DELETE) — see docs/ADMIN-PORT-PLAN.md's endpoint-mapping notes.
|
||||
// `method` lets that call site keep using this composable too.
|
||||
if (opts?.method === 'post') {
|
||||
await api.post(url);
|
||||
} else {
|
||||
await api.delete(url);
|
||||
}
|
||||
message[successType](successMessage);
|
||||
await onSuccess();
|
||||
} catch (e: any) {
|
||||
// The backend refuses deletion while other records still reference the
|
||||
// row (e.g. a city with customers/branches, a brand with products).
|
||||
Modal.error({
|
||||
title: e?.response?.data?.message ?? fallbackErrorMessage,
|
||||
okText: 'بستن',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { remove };
|
||||
}
|
||||
Reference in New Issue
Block a user