feat: complete admin panel and backend admin api migration

This commit is contained in:
s.esmaeili
2026-09-13 21:02:25 +03:30
parent 03119cd655
commit 267fc7b7d1
239 changed files with 25431 additions and 935 deletions
@@ -0,0 +1,125 @@
<template>
<!--
Ported from legacy henza_admin/src/views/workshop/AddWorkshop.vue.
Serves BOTH create and edit — EditWorkshop.vue is a wrapper over it.
Unlike the branch form, this one CREATES its supervisor inline from three
fields (نام / نام خانوادگی / موبایل) rather than picking an existing admin.
The supervisor is saved as an AdminUser with role WORKSHOP_SUPERVISOR.
-->
<a-spin :spinning="loading">
<a-form layout="vertical" @submit.prevent="handleSubmit">
<a-card :title="id ? 'ویرایش کارگاه' : 'افزودن کارگاه'" class="mb-base">
<div class="grid-3">
<a-form-item
label="نام کارگاه"
:validate-status="err.workshop_name ? 'error' : ''"
:help="err.workshop_name"
>
<a-input v-model:value="form.workshop_name" />
</a-form-item>
<a-form-item label="تلفن کارگاه">
<a-input v-model:value="form.workshop_phone" />
</a-form-item>
<a-form-item label="نام سرپرست کارگاه">
<a-input v-model:value="form.user_fname" />
</a-form-item>
<a-form-item label="نام خانوادگی سرپرست کارگاه">
<a-input v-model:value="form.user_lname" />
</a-form-item>
<a-form-item
label="موبایل سرپرست کارگاه"
:validate-status="err.user_phone ? 'error' : ''"
:help="err.user_phone"
>
<a-input v-model:value="form.user_phone" />
</a-form-item>
</div>
<a-form-item label="آدرس">
<a-textarea v-model:value="form.workshop_address" :auto-size="{ minRows: 2, maxRows: 5 }" />
</a-form-item>
<a-button type="primary" @click="handleSubmit">ثبت اطلاعات</a-button>
</a-card>
</a-form>
</a-spin>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { message } from 'ant-design-vue';
import api from '../../api/client';
const route = useRoute();
const router = useRouter();
const id = route.params.id as string | undefined;
const loading = ref(false);
const form = reactive<Record<string, any>>({
workshop_name: '',
workshop_phone: '',
workshop_address: '',
user_fname: '',
user_lname: '',
user_phone: '',
});
const err = reactive<Record<string, string>>({});
async function handleSubmit() {
Object.keys(err).forEach((k) => delete err[k]);
if (!form.workshop_name) err.workshop_name = 'لطفا مقدار را وارد کنید';
// The server requires a mobile whenever any supervisor field is filled.
if ((form.user_fname || form.user_lname) && !form.user_phone) {
err.user_phone = 'موبایل سرپرست کارگاه الزامی است';
}
if (Object.keys(err).length) return;
try {
loading.value = true;
if (id) {
await api.put(`workshop/${id}`, form);
} else {
await api.post('workshop', form);
}
message.info('اطلاعات با موفقیت به ثبت رسید.');
router.push('/workshop/list');
} catch (e: any) {
message.error(e?.response?.data?.message ?? 'خطا در ثبت اطلاعات.');
} finally {
loading.value = false;
}
}
onMounted(async () => {
if (id) {
try {
const { data } = await api.get(`workshop/${id}`);
Object.assign(form, {
workshop_name: data.workshop_name ?? data.name ?? '',
workshop_phone: data.workshop_phone ?? data.phone ?? '',
workshop_address: data.workshop_address ?? data.address ?? '',
user_fname: data.user_fname ?? '',
user_lname: data.user_lname ?? '',
user_phone: data.user_phone ?? '',
});
} catch (e) {
console.log(e);
}
}
});
</script>
<style scoped>
.grid-3 {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1rem;
}
</style>
@@ -0,0 +1,9 @@
<template>
<!-- Ported from legacy views/workshop/EditWorkshop.vue. AddWorkshop reads the
:id route param to switch between create and edit. -->
<AddWorkshop />
</template>
<script setup lang="ts">
import AddWorkshop from './AddWorkshop.vue';
</script>
@@ -0,0 +1,13 @@
// Ported from legacy henza_admin/src/views/workshop/ListWorkshop.js
import XTbl, { Xtc } from '../../components/x-table/model';
export function buildWorkshopListModel(): XTbl {
const tbl = new XTbl('admin/workshops/list', 'لیست کارگاه ها');
tbl.add(new Xtc('name', 'نام').filterText());
tbl.add(new Xtc('address', 'آدرس').filterText());
tbl.add(new Xtc('phone', 'شماره تلفن').filterNumber());
tbl.add(new Xtc('op', 'عملیات').noSort().renderSlot('actions'));
return tbl;
}
export default buildWorkshopListModel;
@@ -0,0 +1,17 @@
<template>
<!-- Ported from legacy views/workshop/ListWorkshop.vue — differs from the
branch list by 14 lines (title, model, edit route). -->
<FacilityList
title="لیست کارگاه ها"
:model="model"
edit-path="/workshop/edit"
active-path="workshop/active"
/>
</template>
<script setup lang="ts">
import FacilityList from '../branch/FacilityList.vue';
import { buildWorkshopListModel } from './ListWorkshop';
const model = buildWorkshopListModel();
</script>