feat: complete admin panel and backend admin api migration
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<!-- Ported from legacy henza_admin/src/views/notify/create.vue. -->
|
||||
<a-spin :spinning="loading">
|
||||
<a-card title="ارسال نوتیفیکیشن">
|
||||
<a-form layout="vertical" @submit.prevent="handleSubmit">
|
||||
<div class="grid-2">
|
||||
<a-form-item label="عنوان" :validate-status="err.title ? 'error' : ''" :help="err.title">
|
||||
<a-input v-model:value="form.title" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="نوع کاربر">
|
||||
<a-select v-model:value="form.type">
|
||||
<a-select-option :value="0">همه مشتریان</a-select-option>
|
||||
<a-select-option :value="2">مشتریان عمده</a-select-option>
|
||||
<a-select-option :value="1">کارشناسان فروش</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
</div>
|
||||
|
||||
<a-form-item label="متن پیام" :validate-status="err.text ? 'error' : ''" :help="err.text">
|
||||
<a-textarea v-model:value="form.text" :auto-size="{ minRows: 4, maxRows: 8 }" />
|
||||
</a-form-item>
|
||||
|
||||
<a-button type="primary" @click="handleSubmit">ارسال</a-button>
|
||||
</a-form>
|
||||
</a-card>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import api from '../../api/client';
|
||||
|
||||
const loading = ref(false);
|
||||
const form = reactive<Record<string, any>>({ title: '', text: '', type: 0 });
|
||||
const err = reactive<Record<string, string>>({});
|
||||
|
||||
async function handleSubmit() {
|
||||
Object.keys(err).forEach((k) => delete err[k]);
|
||||
if (!form.title) err.title = 'عنوان الزامی است';
|
||||
if (!form.text) err.text = 'متن پیام الزامی است';
|
||||
if (Object.keys(err).length) return;
|
||||
|
||||
try {
|
||||
loading.value = true;
|
||||
const { data } = await api.post('notify', form);
|
||||
message.success('نوتیفیکیشن ارسال شد');
|
||||
form.title = '';
|
||||
form.text = '';
|
||||
} catch (e: any) {
|
||||
message.error(e?.response?.data?.message ?? 'خطا در ارسال');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; }
|
||||
</style>
|
||||
@@ -0,0 +1,15 @@
|
||||
// Ported from legacy henza_admin/src/views/notify/notifyTbl.js
|
||||
import XTbl, { Xtc } from '../../components/x-table/model';
|
||||
|
||||
export function buildNotifyModel(): XTbl {
|
||||
const tbl = new XTbl('admin/misc/notify/list', 'لیست نوتیفیکیشن های دریافتی');
|
||||
tbl.add(new Xtc('id', 'شماره'));
|
||||
tbl.add(new Xtc('title', 'عنوان'));
|
||||
// legacy column key is 'note' but the payload carries the body as `text`
|
||||
tbl.add(new Xtc('text', 'متن').noSort());
|
||||
tbl.add(new Xtc('opt', 'عملیات').noSort().renderSlot('opt'));
|
||||
tbl.noFilters();
|
||||
return tbl;
|
||||
}
|
||||
|
||||
export default buildNotifyModel;
|
||||
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<!--
|
||||
Ported from legacy henza_admin/src/views/notify/user.vue — the received
|
||||
notifications list, with a read-state modal.
|
||||
-->
|
||||
<a-card title="لیست نوتیفیکیشن های دریافتی">
|
||||
<XTable ref="tbl" :model="model">
|
||||
<template #opt="{ record }">
|
||||
<a-tooltip title="مشاهده">
|
||||
<EyeOutlined class="cursor-pointer mx-1 text-primary" @click="view(record)" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</XTable>
|
||||
|
||||
<a-modal v-model:open="visible" title="مشاهده نوتیفیکیشن" width="60%">
|
||||
<div v-if="tempRow">
|
||||
<a-descriptions bordered size="small" :column="1">
|
||||
<a-descriptions-item label="عنوان">{{ tempRow.title }}</a-descriptions-item>
|
||||
<a-descriptions-item label="متن">{{ tempRow.body }}</a-descriptions-item>
|
||||
<a-descriptions-item label="ارسال کننده">{{ tempRow.sender ?? '-' }}</a-descriptions-item>
|
||||
<a-descriptions-item label="خوانده شده">
|
||||
{{ tempRow.read_count }} از {{ tempRow.total_count }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
|
||||
<a-table
|
||||
class="mt-4"
|
||||
size="small"
|
||||
:data-source="tempRow.recipients"
|
||||
:columns="recipientColumns"
|
||||
:pagination="{ pageSize: 10 }"
|
||||
row-key="id"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.dataIndex === 'is_read'">
|
||||
<a-tag v-if="record.is_read" color="green">خوانده شده</a-tag>
|
||||
<a-button v-else size="small" @click="markRead(record)">علامتگذاری</a-button>
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'customer'">
|
||||
{{ record.customer.name }}
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
<a-empty v-else />
|
||||
|
||||
<template #footer>
|
||||
<a-button @click="visible = false">بستن</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { EyeOutlined } from '@ant-design/icons-vue';
|
||||
import XTable from '../../components/x-table/XTable.vue';
|
||||
import { buildNotifyModel } from './notifyTbl';
|
||||
import api from '../../api/client';
|
||||
|
||||
const model = buildNotifyModel();
|
||||
const tbl = ref<{ fetch: () => Promise<void> } | null>(null);
|
||||
const visible = ref(false);
|
||||
const tempRow = ref<Record<string, any> | undefined>(undefined);
|
||||
|
||||
const recipientColumns = [
|
||||
{ title: 'مشتری', dataIndex: 'customer' },
|
||||
{ title: 'وضعیت', dataIndex: 'is_read' },
|
||||
];
|
||||
|
||||
async function view(record: Record<string, any>) {
|
||||
const { data } = await api.get(`notify/${record.id}`);
|
||||
tempRow.value = data;
|
||||
visible.value = true;
|
||||
}
|
||||
|
||||
/** legacy GET notify/read/{id} */
|
||||
async function markRead(recipient: Record<string, any>) {
|
||||
try {
|
||||
await api.get(`notify/read/${recipient.id}`);
|
||||
recipient.is_read = true;
|
||||
if (tempRow.value) tempRow.value.read_count += 1;
|
||||
message.success('علامتگذاری شد');
|
||||
} catch (e: any) {
|
||||
message.error(e?.response?.data?.message ?? 'خطا');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mt-4 { margin-top: 1rem; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user