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:
s.esmaeili
2026-09-14 15:18:17 +03:30
co-authored by Claude Sonnet 5
parent f0de478940
commit 02d92fc465
11 changed files with 321 additions and 101 deletions
+9 -12
View File
@@ -25,7 +25,7 @@
placement="top"
ok-text="بله"
cancel-text="خیر"
@confirm="confirmDelete(record.id)"
@confirm="confirmDelete(`cities/${record.id}`)"
>
<template #title><p>آیا مطمئن به حذف شهر مورد نظر هستید ؟</p></template>
<DeleteOutlined class="cursor-pointer mx-1 text-danger" />
@@ -53,11 +53,12 @@
<script setup lang="ts">
import { reactive, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { DeleteOutlined, EditOutlined } from '@ant-design/icons-vue';
import XTable from '../../components/x-table/XTable.vue';
import { buildCityListModel } from './CityListModel';
import { useUsersStore } from '../../stores/users';
import { useDeleteHandler } from '../../composables/useDeleteHandler';
import api from '../../api/client';
const users = useUsersStore();
@@ -106,15 +107,11 @@ async function save() {
}
}
async function confirmDelete(id: string) {
try {
await api.delete(`cities/${id}`);
message.success('شهر حذف شد');
citiesTable.value?.fetch();
} catch (e: any) {
// The backend refuses while customers, branches or addresses reference it.
Modal.error({ title: e?.response?.data?.message ?? 'خطا در حذف شهر', okText: 'بستن' });
}
}
// The backend refuses while customers, branches or addresses reference it.
const { remove: confirmDelete } = useDeleteHandler({
onSuccess: () => citiesTable.value?.fetch(),
successMessage: 'شهر حذف شد',
fallbackErrorMessage: 'خطا در حذف شهر',
});
</script>