darta page added

This commit is contained in:
prabidhi 2025-12-10 15:44:28 +05:45
parent 9f6c30541e
commit 67930dfbbb
8 changed files with 453 additions and 15 deletions

View File

@ -10,6 +10,14 @@ const authChildren: Array<RouteRecordRaw> = [
permission: "",
},
},
{
path: "/darta",
name: "darta",
component: () => import("@/views/Darta/Darta.vue"),
meta: {
permission: "",
},
}
];
export default authChildren;

188
src/stores/GenericStore.ts Normal file
View File

@ -0,0 +1,188 @@
import { defineStore } from "pinia";
import Swal from "sweetalert2";
import api from "@/services/API/api";
import type { Ref } from "vue";
import { Toast } from "dolphin-components";
interface GenericStoreState {
errors: Record<string, any>;
itemList: [];
currentItemDetail: Record<string, any>;
payload: Record<string, any>;
selectedItems: Record<string, any>;
}
export const createGenericStore = (storeId: string) => {
return defineStore(storeId, {
state: (): GenericStoreState => ({
errors: {},
itemList: [],
currentItemDetail: {},
payload: {},
selectedItems: {},
}),
actions: {
async fetchList(
apiEndpoint: string,
params: Record<string, any> = {},
loadingRef: Ref<boolean> | null = null
): Promise<boolean> {
this.errors = {};
if (loadingRef) {
loadingRef.value = true;
}
let response = false;
try {
const apiResponse = await api.get(apiEndpoint, { params });
this.itemList = apiResponse.data.data;
response = true;
} catch (error: any) {
this.errors = error?.response?.data?.errors;
this.itemList = [];
if (error?.response?.status == 403) {
Toast.error(
error.response.data.detail || "You do not have the permissions to perform this action"
);
}
Toast.error(error?.response?.data?.message || "Error Fetching List");
}
if (loadingRef) {
loadingRef.value = false;
}
return response;
},
async fetchDetail(
apiEndpoint: string,
id: number | string,
loadingRef: Ref<boolean> | null = null,
params: Record<string, any> = {}
): Promise<boolean> {
this.errors = {};
this.currentItemDetail = {};
if (loadingRef) loadingRef.value = true;
let response = false;
try {
const apiResponse = await api.get(apiEndpoint + id + "/", { params });
this.currentItemDetail = apiResponse.data.data;
response = true;
} catch (error: any) {
this.errors = error?.response?.data?.errors;
if (error?.response?.status == 403) {
Toast.error(
error.response.data.detail || "You do not have the permissions to perform this action"
);
}
Toast.error(error?.response?.data?.message || "Error Fetching Detail");
}
if (loadingRef) loadingRef.value = false;
return response;
},
async createItem(
apiEndpoint: string,
fetchList: boolean = true,
loadingRef: Ref<boolean> | null = null
): Promise<any> {
this.errors = {};
if (loadingRef) loadingRef.value = true;
let response: any = false;
try {
response = await api.post(apiEndpoint, this.payload);
if (fetchList) {
await this.fetchList(apiEndpoint);
}
this.resetPayload();
if (loadingRef) loadingRef.value = false;
} catch (error: any) {
this.errors = error?.response?.data?.errors;
if (error?.response?.status == 403) {
Toast.error(
error.response.data.detail || "You do not have the permissions to perform this action"
);
}
if (error?.response?.data?.message != "Validation Error")
Toast.error(error?.response?.data?.message || "Error Creating Item");
}
if (loadingRef) loadingRef.value = false;
return response?.data?.data ?? false;
},
async updateItem(
apiEndpoint: string,
id: number | string,
fetchList: boolean = true,
loadingRef: Ref<boolean> | null = null
): Promise<boolean> {
this.errors = {};
if (loadingRef) loadingRef.value = true;
let response = false;
try {
await api.patch(apiEndpoint + id + "/", this.payload);
response = true;
if (fetchList) {
await this.fetchList(apiEndpoint);
}
this.resetPayload();
} catch (error: any) {
response = false;
this.errors = error?.response?.data?.errors;
if (error?.response?.status == 403) {
Toast.error(
error?.response?.data?.detail || "You do not have the permissions to perform this action"
);
} else if (error?.response?.data?.message != "Validation Error") {
Toast.error(error?.response?.data?.message || "Error Updating Item");
}
}
if (loadingRef) loadingRef.value = false;
return response;
},
async deleteItem(
apiEndpoint: string,
id: number | string,
fetchList: boolean = true,
loadingRef: Ref<boolean> | null = null
): Promise<boolean> {
const result = await Swal.fire({
title: "Delete Confirmation",
text: "Are you sure you want to delete this item?",
showCancelButton: true,
confirmButtonText: "Confirm",
confirmButtonColor: "#004cad",
cancelButtonColor: "#9e9e9e",
});
if (result.isConfirmed) {
if (loadingRef) loadingRef.value = true;
let response = false;
try {
await api.delete(apiEndpoint + id + "/");
response = true;
if (fetchList) {
await this.fetchList(apiEndpoint);
}
} catch (error: any) {
this.errors = error?.response?.data?.errors;
if (error?.response?.status == 403) {
Toast.error(
error.response.data.detail || "You do not have the permissions to perform this action"
);
}
Toast.error(error || "Error Deleting Item");
}
if (loadingRef) loadingRef.value = false;
return response;
}
return false;
},
resetPayload() {
this.payload = {};
this.errors = {};
this.selectedItems = {};
},
},
});
};

123
src/stores/OverlayStore.ts Normal file
View File

@ -0,0 +1,123 @@
import { defineStore } from "pinia";
import { ref, type Ref, onUnmounted } from "vue";
import { CreateShortCutKey } from "dolphin-components";
// We may multiple overlays on top of one another
class EscapeKeyManager {
private static instance: EscapeKeyManager;
private modalStack: Array<() => void> = [];
private shortcutHandler: { destroy: () => void } | null = null;
static getInstance(): EscapeKeyManager {
if (!EscapeKeyManager.instance) {
EscapeKeyManager.instance = new EscapeKeyManager();
}
return EscapeKeyManager.instance;
}
addModal(closeCallback: () => void) {
this.modalStack.push(closeCallback);
if (this.modalStack.length === 1) {
this.setupEscapeKey();
}
}
removeModal(closeCallback: () => void) {
const index = this.modalStack.indexOf(closeCallback);
if (index > -1) {
this.modalStack.splice(index, 1);
}
if (this.modalStack.length === 0) {
this.cleanupEscapeKey();
}
}
private setupEscapeKey() {
if (!this.shortcutHandler) {
this.shortcutHandler = CreateShortCutKey("escape", () => {
if (this.modalStack.length > 0) {
const topModalClose = this.modalStack[this.modalStack.length - 1];
if (typeof topModalClose == "function") {
topModalClose();
}
}
});
}
}
private cleanupEscapeKey() {
if (this.shortcutHandler) {
this.shortcutHandler.destroy();
this.shortcutHandler = null;
}
}
}
export const createOverlayStore = (storeId: string) => {
return defineStore(storeId, () => {
const show = ref(false);
const editId = ref<number | string | null>(null);
const context = ref<Record<string, any>>({});
const escapeManager = EscapeKeyManager.getInstance();
let onCloseCallback: (() => void) | null = null;
function open(options: { id?: number | string; context?: Record<string, any> } = {}) {
editId.value = options.id ?? null;
context.value = {
...context.value,
...(options.context || {}),
};
show.value = true;
escapeManager.addModal(close);
}
function close() {
if (onCloseCallback) {
onCloseCallback();
}
show.value = false;
editId.value = null;
escapeManager.removeModal(close);
}
async function onSave(
genericStore: any,
genericAPI: any,
fetchList: boolean = true,
loadingRef: Ref<boolean> | null = null
) {
let response: any = false;
if (editId.value !== null) {
response = await genericStore.updateItem(genericAPI, editId.value, fetchList, loadingRef);
} else {
response = await genericStore.createItem(genericAPI, fetchList, loadingRef);
}
if (response) close();
return response;
}
onUnmounted(() => {
if (show.value) {
escapeManager.removeModal(close);
}
});
function setOnCloseCallback(callback: () => void) {
onCloseCallback = callback;
}
return {
show,
editId,
context,
open,
close,
onSave,
setOnCloseCallback,
};
});
};

View File

@ -1,12 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})

View File

@ -7,7 +7,7 @@
<div class="border border-secondary-100 rounded-xs px-10 py-10 bg-white select-none">
<div class="text-center" :class="getLoginError ? '' : 'mb-[10px]'">
<p class="text-[30px] text-semibold">Sign In</p>
<div class="text-sm font-normal">Fill your detail to sign in to Dolphin DartaChalani.</div>
<div class="text-sm font-normal">Fill your detail to sign in to Dolphin Dartachalani.</div>
</div>
<div class="mt-[12px] mb-[-19px]" v-if="getLoginError">

View File

@ -0,0 +1,33 @@
<template>
<Modal :title="modal.title" :actions="modal.action" @onClose="onClose"></Modal>
</template>
<script setup lang="ts">
import { ref } from "vue";
const modal = ref({
show: false,
title: [
{
name: "Darta",
link: "#",
},
{
name: "Create",
},
],
action: [
{
title: "Close",
emit: "onClose",
class: "btn-outline",
},
{
title: "Save",
emit: "onSave",
},
],
});
const onClose = () => {};
</script>

98
src/views/Darta/Darta.vue Normal file
View File

@ -0,0 +1,98 @@
<template>
<ContentLayout :title="layout.title" :actions="layout.action" @onCreate="onCreate">
<template #body>
<div class="content-search">
<DateSelector id="report-date" v-model="selectedDate" :classValue="`w-full`" :isBS="true" />
</div>
<Tabulator
:columns="headerDetails"
:data="itemList"
:heightOffset="250"
:action="true"
placeholder="No Darta Found"
/>
</template>
</ContentLayout>
<Create />
</template>
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
import { BStoAD, NepaliDate } from "nepali-date-library";
import { storeToRefs } from "pinia";
import Create from "@/views/Darta/Create.vue";
import { createGenericStore } from "@/stores/GenericStore";
import { createOverlayStore } from "@/stores/OverlayStore";
import { Toast } from "dolphin-components";
const DartaStore = createGenericStore("darta")();
const DartaModalStore = createOverlayStore("darta-modal")();
const { itemList } = storeToRefs(DartaStore);
const layout = ref({
title: [
{
name: "Darta",
},
],
action: [
{
title: "Create",
emit: "onCreate",
},
],
});
// const timeFormatter = (cell: any) => {
// const timeStr = cell.getValue();
// if (timeStr) {
// const [hours, minutes] = timeStr.split(":").map(Number);
// const date = new Date(0, 0, 0, hours, minutes);
// return date.toLocaleTimeString([], {
// hour: "numeric",
// minute: "2-digit",
// hour12: true,
// });
// } else {
// return cell.getValue();
// }
// };
const selectedDate = ref(BStoAD(new NepaliDate().format("YYYY-MM-DD")));
const headerDetails = [
{
field: "document_no",
title: "Document Number",
headerFilter: true,
headerFilterPlaceholder: "Search",
},
{
field: "sender_name",
title: "Sender",
headerFilter: true,
headerFilterPlaceholder: "Search",
},
{
field: "receiver_name",
title: "Receiver",
headerFilter: true,
headerFilterPlaceholder: "Search",
},
{
field: "received_date",
title: "Received Date",
},
{
field: "is_active",
title: "Status",
width: "250",
},
];
const onCreate = () => {
DartaModalStore.open();
};
</script>

View File

@ -8,7 +8,7 @@
</div>
</div>
<div class="p-[15px] bg-white relative">
<div class="text-[#2f2f2f]">Incoming (Darta)</div>
<div class="text-[#2f2f2f]">Darta (Incoming)</div>
<div class="text-[24px] text-[#333131]">
{{ data.incoming }}
</div>
@ -19,7 +19,7 @@
</div>
<div class="p-[15px] bg-white relative">
<div class="w-fit">
<div class="text-[#2f2f2f]">Outgoing (Chalani)</div>
<div class="text-[#2f2f2f]">Chalani (Outgoing)</div>
<div class="text-[24px] text-[#333131]">
{{ data.outgoing }}
</div>