refactor: standardize table row actions by implementing reusable RowEditAction and RowDeleteAction components
This commit is contained in:
parent
06e313d168
commit
2f8ece4b0a
28
resources/js/components/button/BackButton.vue
Normal file
28
resources/js/components/button/BackButton.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Link } from '@inertiajs/vue3';
|
||||||
|
import { ArrowLeft } from '@lucide/vue';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
href?: string;
|
||||||
|
label?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
label: 'Kembali',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button variant="outline" :as-child="!!href" class="shrink-0 self-start sm:self-center">
|
||||||
|
<Link v-if="href" :href="href">
|
||||||
|
<ArrowLeft class="size-4" />
|
||||||
|
<slot>{{ label }}</slot>
|
||||||
|
</Link>
|
||||||
|
<template v-else>
|
||||||
|
<ArrowLeft class="size-4" />
|
||||||
|
<slot>{{ label }}</slot>
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
28
resources/js/components/button/CreateButton.vue
Normal file
28
resources/js/components/button/CreateButton.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Link } from '@inertiajs/vue3';
|
||||||
|
import { Plus } from '@lucide/vue';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
|
withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
href?: string;
|
||||||
|
label?: string;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
label: 'Tambah',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Button :as-child="!!href" class="shrink-0 self-start sm:self-center">
|
||||||
|
<Link v-if="href" :href="href">
|
||||||
|
<Plus class="size-4" />
|
||||||
|
<slot>{{ label }}</slot>
|
||||||
|
</Link>
|
||||||
|
<template v-else>
|
||||||
|
<Plus class="size-4" />
|
||||||
|
<slot>{{ label }}</slot>
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
22
resources/js/components/button/RowApproveAction.vue
Normal file
22
resources/js/components/button/RowApproveAction.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Check } from '@lucide/vue';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
tooltip?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger as-child>
|
||||||
|
<Button variant="ghost" size="icon" class="size-8 text-green-600 hover:text-green-700" :disabled="disabled">
|
||||||
|
<Check class="size-4" />
|
||||||
|
<span class="sr-only">{{ tooltip || 'Setujui' }}</span>
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>{{ tooltip || 'Setujui' }}</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</template>
|
||||||
61
resources/js/components/button/RowDeleteAction.vue
Normal file
61
resources/js/components/button/RowDeleteAction.vue
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { router } from '@inertiajs/vue3';
|
||||||
|
import { Trash2 } from '@lucide/vue';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { toast } from 'vue-sonner';
|
||||||
|
import ConfirmDialog from '@/components/ConfirmDialog.vue';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
actionUrl?: string; // If provided, handles the deletion request automatically.
|
||||||
|
errorMessage?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
confirm: [];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const deleteConfirmOpen = ref(false);
|
||||||
|
const deleteProcessing = ref(false);
|
||||||
|
|
||||||
|
function handleConfirm() {
|
||||||
|
if (props.actionUrl) {
|
||||||
|
deleteProcessing.value = true;
|
||||||
|
router.delete(props.actionUrl, {
|
||||||
|
preserveScroll: true,
|
||||||
|
onSuccess: () => {
|
||||||
|
deleteConfirmOpen.value = false;
|
||||||
|
},
|
||||||
|
onError: (errors) => {
|
||||||
|
const firstError = Object.values(errors)[0];
|
||||||
|
toast.error(firstError || props.errorMessage || 'Gagal menghapus data.');
|
||||||
|
},
|
||||||
|
onFinish: () => {
|
||||||
|
deleteProcessing.value = false;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
emit('confirm');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger as-child>
|
||||||
|
<Button variant="ghost" size="icon" class="text-destructive hover:text-destructive size-8"
|
||||||
|
@click="deleteConfirmOpen = true">
|
||||||
|
<Trash2 class="size-4" />
|
||||||
|
<span class="sr-only">Hapus</span>
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>Hapus</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<ConfirmDialog v-model:open="deleteConfirmOpen" :title="title || 'Hapus data?'"
|
||||||
|
:description="description || 'Data akan dihapus secara permanen. Tindakan ini tidak dapat dibatalkan.'"
|
||||||
|
confirm-label="Hapus" cancel-label="Batal" destructive :loading="deleteProcessing" @confirm="handleConfirm" />
|
||||||
|
</template>
|
||||||
29
resources/js/components/button/RowDetailAction.vue
Normal file
29
resources/js/components/button/RowDetailAction.vue
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Link } from '@inertiajs/vue3';
|
||||||
|
import { Eye } from '@lucide/vue';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
href?: string;
|
||||||
|
tooltip?: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger as-child>
|
||||||
|
<Button variant="ghost" size="icon" class="size-8" :as-child="!!href">
|
||||||
|
<Link v-if="href" :href="href">
|
||||||
|
<Eye class="size-4" />
|
||||||
|
<span class="sr-only">{{ tooltip || 'Detail' }}</span>
|
||||||
|
</Link>
|
||||||
|
<template v-else>
|
||||||
|
<Eye class="size-4" />
|
||||||
|
<span class="sr-only">{{ tooltip || 'Detail' }}</span>
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>{{ tooltip || 'Detail' }}</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</template>
|
||||||
28
resources/js/components/button/RowEditAction.vue
Normal file
28
resources/js/components/button/RowEditAction.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Link } from '@inertiajs/vue3';
|
||||||
|
import { Pencil } from '@lucide/vue';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
href?: string;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger as-child>
|
||||||
|
<Button variant="ghost" size="icon" class="size-8" :as-child="!!href">
|
||||||
|
<Link v-if="href" :href="href">
|
||||||
|
<Pencil class="size-4" />
|
||||||
|
<span class="sr-only">Ubah</span>
|
||||||
|
</Link>
|
||||||
|
<template v-else>
|
||||||
|
<Pencil class="size-4" />
|
||||||
|
<span class="sr-only">Ubah</span>
|
||||||
|
</template>
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>Ubah</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</template>
|
||||||
22
resources/js/components/button/RowRejectAction.vue
Normal file
22
resources/js/components/button/RowRejectAction.vue
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { X } from '@lucide/vue';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
tooltip?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger as-child>
|
||||||
|
<Button variant="ghost" size="icon" class="size-8 text-destructive hover:text-destructive" :disabled="disabled">
|
||||||
|
<X class="size-4" />
|
||||||
|
<span class="sr-only">{{ tooltip || 'Tolak' }}</span>
|
||||||
|
</Button>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>{{ tooltip || 'Tolak' }}</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</template>
|
||||||
7
resources/js/components/button/index.ts
Normal file
7
resources/js/components/button/index.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export { default as RowEditAction } from './RowEditAction.vue';
|
||||||
|
export { default as RowDeleteAction } from './RowDeleteAction.vue';
|
||||||
|
export { default as RowApproveAction } from './RowApproveAction.vue';
|
||||||
|
export { default as RowRejectAction } from './RowRejectAction.vue';
|
||||||
|
export { default as RowDetailAction } from './RowDetailAction.vue';
|
||||||
|
export { default as CreateButton } from './CreateButton.vue';
|
||||||
|
export { default as BackButton } from './BackButton.vue';
|
||||||
@ -1,19 +1,25 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
|
import { DataTable } from '@/components/data-table';
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { useCan } from '@/composables/useCan';
|
||||||
|
import {
|
||||||
|
useDataTableQuery,
|
||||||
|
useDataTableQuerySync,
|
||||||
|
} from '@/composables/useDataTableQuery';
|
||||||
|
import { EmployeeAdvanceStatus } from '@/constants/employee-advance-status';
|
||||||
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
|
import type { DataTableFilterDef, DataTableSort } from '@/types/data-table';
|
||||||
|
import type {
|
||||||
|
EmployeeAdvanceListItem,
|
||||||
|
EmployeeAdvancePageProps,
|
||||||
|
} from '@/types/employee-advance';
|
||||||
import { Head } from '@inertiajs/vue3';
|
import { Head } from '@inertiajs/vue3';
|
||||||
import { HandCoins, Plus } from '@lucide/vue';
|
import { HandCoins } from '@lucide/vue';
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import EmployeeAdvanceFormModal from './form/EmployeeAdvanceFormModal.vue';
|
import EmployeeAdvanceFormModal from './form/EmployeeAdvanceFormModal.vue';
|
||||||
import RejectEmployeeAdvanceModal from './form/RejectEmployeeAdvanceModal.vue';
|
import RejectEmployeeAdvanceModal from './form/RejectEmployeeAdvanceModal.vue';
|
||||||
import { createColumns } from './table/columns';
|
import { createColumns } from './table/columns';
|
||||||
import { DataTable } from '@/components/data-table';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
||||||
import { useCan } from '@/composables/useCan';
|
|
||||||
import { EmployeeAdvanceStatus } from '@/constants/employee-advance-status';
|
|
||||||
import { useDataTableQuery, useDataTableQuerySync } from '@/composables/useDataTableQuery';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
|
||||||
import type { DataTableFilterDef, DataTableSort } from '@/types/data-table';
|
|
||||||
import type { EmployeeAdvanceListItem, EmployeeAdvancePageProps } from '@/types/employee-advance';
|
|
||||||
|
|
||||||
const props = defineProps<EmployeeAdvancePageProps>();
|
const props = defineProps<EmployeeAdvancePageProps>();
|
||||||
|
|
||||||
@ -24,19 +30,18 @@ const rejectModalOpen = ref(false);
|
|||||||
const editingEmployeeAdvance = ref<EmployeeAdvanceListItem | null>(null);
|
const editingEmployeeAdvance = ref<EmployeeAdvanceListItem | null>(null);
|
||||||
const rejectingEmployeeAdvance = ref<EmployeeAdvanceListItem | null>(null);
|
const rejectingEmployeeAdvance = ref<EmployeeAdvanceListItem | null>(null);
|
||||||
|
|
||||||
const { query, setSearch, setSort, setFilter, resetFilters, syncFromServer } = useDataTableQuery({
|
const { query, setSearch, setSort, setFilter, resetFilters, syncFromServer } =
|
||||||
url: '/admin/finance/employee-advances',
|
useDataTableQuery({
|
||||||
initial: { ...props.filters },
|
url: '/admin/finance/employee-advances',
|
||||||
filterKeys: ['status'],
|
initial: { ...props.filters },
|
||||||
});
|
filterKeys: ['status'],
|
||||||
|
});
|
||||||
|
|
||||||
useDataTableQuerySync(() => props.filters, syncFromServer);
|
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||||
|
|
||||||
const columns = computed(() => createColumns(
|
const columns = computed(() =>
|
||||||
props.authEmployeeId,
|
createColumns(props.authEmployeeId, openEditModal, openRejectModal),
|
||||||
openEditModal,
|
);
|
||||||
openRejectModal,
|
|
||||||
));
|
|
||||||
|
|
||||||
const currentSort = computed<DataTableSort | null>(() => {
|
const currentSort = computed<DataTableSort | null>(() => {
|
||||||
if (!query.value.sort || !query.value.direction) {
|
if (!query.value.sort || !query.value.direction) {
|
||||||
@ -102,25 +107,27 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Kasbon" />
|
<Head title="Kasbon" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Kasbon</h2>
|
||||||
Kasbon
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="canSubmit" class="shrink-0 self-start sm:self-center" @click="openCreateModal">
|
<CreateButton
|
||||||
<Plus class="size-4" />
|
v-if="canSubmit"
|
||||||
Ajukan
|
@click="openCreateModal"
|
||||||
</Button>
|
label="Ajukan"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card v-if="can('employee_advances.verify')">
|
<Card v-if="can('employee_advances.verify')">
|
||||||
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
<CardHeader
|
||||||
|
class="flex flex-row items-center justify-between space-y-0 pb-2"
|
||||||
|
>
|
||||||
<CardTitle class="text-sm font-medium text-muted-foreground">
|
<CardTitle class="text-sm font-medium text-muted-foreground">
|
||||||
Total Kasbon
|
Total Kasbon
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
@ -138,17 +145,32 @@ watch(
|
|||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
<CardContent class="min-w-0 pt-6">
|
<CardContent class="min-w-0 pt-6">
|
||||||
<DataTable v-model:search="search" :columns="columns" :data="employeeAdvances.data"
|
<DataTable
|
||||||
:pagination="pagination" :pagination-links="employeeAdvances.links" :sort="currentSort"
|
v-model:search="search"
|
||||||
:filter-defs="filterDefs" :filter-values="filterValues" @sort-change="setSort"
|
:columns="columns"
|
||||||
@filter-change="setFilter" @filters-reset="resetFilters" />
|
:data="employeeAdvances.data"
|
||||||
|
:pagination="pagination"
|
||||||
|
:pagination-links="employeeAdvances.links"
|
||||||
|
:sort="currentSort"
|
||||||
|
:filter-defs="filterDefs"
|
||||||
|
:filter-values="filterValues"
|
||||||
|
@sort-change="setSort"
|
||||||
|
@filter-change="setFilter"
|
||||||
|
@filters-reset="resetFilters"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<EmployeeAdvanceFormModal v-if="canSubmit" v-model:open="formModalOpen"
|
<EmployeeAdvanceFormModal
|
||||||
:employee-advance="editingEmployeeAdvance" />
|
v-if="canSubmit"
|
||||||
|
v-model:open="formModalOpen"
|
||||||
|
:employee-advance="editingEmployeeAdvance"
|
||||||
|
/>
|
||||||
|
|
||||||
<RejectEmployeeAdvanceModal v-if="can('employee_advances.verify')" v-model:open="rejectModalOpen"
|
<RejectEmployeeAdvanceModal
|
||||||
:employee-advance="rejectingEmployeeAdvance" />
|
v-if="can('employee_advances.verify')"
|
||||||
|
v-model:open="rejectModalOpen"
|
||||||
|
:employee-advance="rejectingEmployeeAdvance"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import ExpenseFormModal from './form/ExpenseFormModal.vue';
|
|
||||||
import { createColumns } from './table/columns';
|
|
||||||
import { DataTable } from '@/components/data-table';
|
import { DataTable } from '@/components/data-table';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import { useDataTableQuery, useDataTableQuerySync } from '@/composables/useDataTableQuery';
|
import {
|
||||||
|
useDataTableQuery,
|
||||||
|
useDataTableQuerySync,
|
||||||
|
} from '@/composables/useDataTableQuery';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { DataTableSort } from '@/types/data-table';
|
import type { DataTableSort } from '@/types/data-table';
|
||||||
import type { ExpenseListItem, PaginatedExpenses } from '@/types/expense';
|
import type { ExpenseListItem, PaginatedExpenses } from '@/types/expense';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import ExpenseFormModal from './form/ExpenseFormModal.vue';
|
||||||
|
import { createColumns } from './table/columns';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
expenses: PaginatedExpenses;
|
expenses: PaginatedExpenses;
|
||||||
@ -27,10 +29,11 @@ const search = ref(props.filters.search ?? '');
|
|||||||
const formModalOpen = ref(false);
|
const formModalOpen = ref(false);
|
||||||
const editingExpense = ref<ExpenseListItem | null>(null);
|
const editingExpense = ref<ExpenseListItem | null>(null);
|
||||||
|
|
||||||
const { query, setSearch, setSort, resetFilters, syncFromServer } = useDataTableQuery({
|
const { query, setSearch, setSort, resetFilters, syncFromServer } =
|
||||||
url: '/admin/finance/expenses',
|
useDataTableQuery({
|
||||||
initial: { ...props.filters },
|
url: '/admin/finance/expenses',
|
||||||
});
|
initial: { ...props.filters },
|
||||||
|
});
|
||||||
|
|
||||||
useDataTableQuerySync(() => props.filters, syncFromServer);
|
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||||
|
|
||||||
@ -77,32 +80,41 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Pengeluaran" />
|
<Head title="Pengeluaran" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Pengeluaran</h2>
|
||||||
Pengeluaran
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="can('expenses.create')" class="shrink-0 self-start sm:self-center" @click="openCreateModal">
|
<CreateButton
|
||||||
<Plus class="size-4" />
|
v-if="can('expenses.create')"
|
||||||
Tambah
|
@click="openCreateModal"
|
||||||
</Button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
<CardContent class="min-w-0">
|
<CardContent class="min-w-0">
|
||||||
<DataTable v-model:search="search" :columns="columns" :data="expenses.data" :pagination="pagination"
|
<DataTable
|
||||||
:pagination-links="expenses.links" :sort="currentSort" @sort-change="setSort"
|
v-model:search="search"
|
||||||
@filters-reset="resetFilters" />
|
:columns="columns"
|
||||||
|
:data="expenses.data"
|
||||||
|
:pagination="pagination"
|
||||||
|
:pagination-links="expenses.links"
|
||||||
|
:sort="currentSort"
|
||||||
|
@sort-change="setSort"
|
||||||
|
@filters-reset="resetFilters"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<ExpenseFormModal v-if="can('expenses.create') || can('expenses.update')" v-model:open="formModalOpen"
|
<ExpenseFormModal
|
||||||
:expense="editingExpense" />
|
v-if="can('expenses.create') || can('expenses.update')"
|
||||||
|
v-model:open="formModalOpen"
|
||||||
|
:expense="editingExpense"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import EmployeeForm from './form/EmployeeForm.vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { EnumOption } from '@/types/employee';
|
import type { EnumOption } from '@/types/employee';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import EmployeeForm from './form/EmployeeForm.vue';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
genders: EnumOption[];
|
genders: EnumOption[];
|
||||||
@ -17,19 +16,23 @@ defineProps<{
|
|||||||
<Head title="Tambah Pegawai" />
|
<Head title="Tambah Pegawai" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">Tambah Pegawai</h2>
|
<h2 class="text-2xl font-bold tracking-tight">
|
||||||
|
Tambah Pegawai
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/hr/employees" />
|
||||||
<Link href="/admin/hr/employees">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EmployeeForm submit-url="/admin/hr/employees" :genders="genders" :employment-statuses="employmentStatuses" :roles="roles" />
|
<EmployeeForm
|
||||||
|
submit-url="/admin/hr/employees"
|
||||||
|
:genders="genders"
|
||||||
|
:employment-statuses="employmentStatuses"
|
||||||
|
:roles="roles"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import EmployeeForm from './form/EmployeeForm.vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { EmployeeListItem, EnumOption } from '@/types/employee';
|
import type { EmployeeListItem, EnumOption } from '@/types/employee';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import EmployeeForm from './form/EmployeeForm.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
employee: EmployeeListItem;
|
employee: EmployeeListItem;
|
||||||
@ -24,33 +23,38 @@ const initialData = computed(() => ({
|
|||||||
address: props.employee.profile?.address ?? '',
|
address: props.employee.profile?.address ?? '',
|
||||||
join_date: props.employee.employee?.join_date_input ?? '',
|
join_date: props.employee.employee?.join_date_input ?? '',
|
||||||
employment_status: props.employee.employee?.employment_status ?? '',
|
employment_status: props.employee.employee?.employment_status ?? '',
|
||||||
base_salary: props.employee.employee?.base_salary != null ? String(props.employee.employee.base_salary) : '',
|
base_salary:
|
||||||
|
props.employee.employee?.base_salary != null
|
||||||
|
? String(props.employee.employee.base_salary)
|
||||||
|
: '',
|
||||||
role: props.employee.roles?.[0]?.name ?? '',
|
role: props.employee.roles?.[0]?.name ?? '',
|
||||||
}));
|
}));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Ubah Pegawai" />
|
<Head title="Ubah Pegawai" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Ubah Pegawai</h2>
|
||||||
Ubah Pegawai
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/hr/employees" />
|
||||||
<Link href="/admin/hr/employees">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EmployeeForm :submit-url="`/admin/hr/employees/${employee.id}`" method="put" submit-label="Perbarui"
|
<EmployeeForm
|
||||||
:initial-data="initialData" :employee-id="employee.id" :employee-name="employee.profile?.full_name"
|
:submit-url="`/admin/hr/employees/${employee.id}`"
|
||||||
:genders="genders" :employment-statuses="employmentStatuses" :roles="roles" />
|
method="put"
|
||||||
|
submit-label="Perbarui"
|
||||||
|
:initial-data="initialData"
|
||||||
|
:employee-id="employee.id"
|
||||||
|
:employee-name="employee.profile?.full_name"
|
||||||
|
:genders="genders"
|
||||||
|
:employment-statuses="employmentStatuses"
|
||||||
|
:roles="roles"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,20 +1,19 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import { columns } from './table/columns';
|
|
||||||
import { DataTable } from '@/components/data-table';
|
import { DataTable } from '@/components/data-table';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import { ActiveStatus } from '@/constants/active-status';
|
|
||||||
import {
|
import {
|
||||||
useDataTableQuery,
|
useDataTableQuery,
|
||||||
useDataTableQuerySync,
|
useDataTableQuerySync,
|
||||||
} from '@/composables/useDataTableQuery';
|
} from '@/composables/useDataTableQuery';
|
||||||
|
import { ActiveStatus } from '@/constants/active-status';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { DataTableFilterDef, DataTableSort } from '@/types/data-table';
|
import type { DataTableFilterDef, DataTableSort } from '@/types/data-table';
|
||||||
import type { EnumOption, PaginatedEmployees } from '@/types/employee';
|
import type { EnumOption, PaginatedEmployees } from '@/types/employee';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import { columns } from './table/columns';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
employees: PaginatedEmployees;
|
employees: PaginatedEmployees;
|
||||||
@ -35,11 +34,12 @@ const props = defineProps<{
|
|||||||
const { can } = useCan();
|
const { can } = useCan();
|
||||||
const search = ref(props.filters.search ?? '');
|
const search = ref(props.filters.search ?? '');
|
||||||
|
|
||||||
const { query, setSearch, setSort, setFilter, resetFilters, syncFromServer } = useDataTableQuery({
|
const { query, setSearch, setSort, setFilter, resetFilters, syncFromServer } =
|
||||||
url: '/admin/hr/employees',
|
useDataTableQuery({
|
||||||
initial: { ...props.filters },
|
url: '/admin/hr/employees',
|
||||||
filterKeys: ['role', 'gender', 'employment_status', 'is_active'],
|
initial: { ...props.filters },
|
||||||
});
|
filterKeys: ['role', 'gender', 'employment_status', 'is_active'],
|
||||||
|
});
|
||||||
|
|
||||||
useDataTableQuerySync(() => props.filters, syncFromServer);
|
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||||
|
|
||||||
@ -114,17 +114,17 @@ watch(
|
|||||||
<Head title="Pegawai" />
|
<Head title="Pegawai" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">Pegawai</h2>
|
<h2 class="text-2xl font-bold tracking-tight">Pegawai</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="can('employees.create')" as-child class="shrink-0 self-start sm:self-center">
|
<CreateButton
|
||||||
<Link href="/admin/hr/employees/create">
|
v-if="can('employees.create')"
|
||||||
<Plus class="size-4" />
|
href="/admin/hr/employees/create"
|
||||||
Tambah
|
/>
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
|
|||||||
@ -1,12 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import LeaveRequestFormModal from './form/LeaveRequestFormModal.vue';
|
|
||||||
import RejectLeaveRequestModal from './form/RejectLeaveRequestModal.vue';
|
|
||||||
import { createColumns } from './table/columns';
|
|
||||||
import { DataTable } from '@/components/data-table';
|
import { DataTable } from '@/components/data-table';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import {
|
import {
|
||||||
@ -15,7 +9,15 @@ import {
|
|||||||
} from '@/composables/useDataTableQuery';
|
} from '@/composables/useDataTableQuery';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { DataTableSort } from '@/types/data-table';
|
import type { DataTableSort } from '@/types/data-table';
|
||||||
import type { LeaveRequestListItem, LeaveRequestPageProps } from '@/types/leave-request';
|
import type {
|
||||||
|
LeaveRequestListItem,
|
||||||
|
LeaveRequestPageProps,
|
||||||
|
} from '@/types/leave-request';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import LeaveRequestFormModal from './form/LeaveRequestFormModal.vue';
|
||||||
|
import RejectLeaveRequestModal from './form/RejectLeaveRequestModal.vue';
|
||||||
|
import { createColumns } from './table/columns';
|
||||||
|
|
||||||
const props = defineProps<LeaveRequestPageProps>();
|
const props = defineProps<LeaveRequestPageProps>();
|
||||||
|
|
||||||
@ -26,18 +28,17 @@ const rejectModalOpen = ref(false);
|
|||||||
const editingLeaveRequest = ref<LeaveRequestListItem | null>(null);
|
const editingLeaveRequest = ref<LeaveRequestListItem | null>(null);
|
||||||
const rejectingLeaveRequest = ref<LeaveRequestListItem | null>(null);
|
const rejectingLeaveRequest = ref<LeaveRequestListItem | null>(null);
|
||||||
|
|
||||||
const { query, setSearch, setSort, resetFilters, syncFromServer } = useDataTableQuery({
|
const { query, setSearch, setSort, resetFilters, syncFromServer } =
|
||||||
url: '/admin/hr/leave-requests',
|
useDataTableQuery({
|
||||||
initial: { ...props.filters },
|
url: '/admin/hr/leave-requests',
|
||||||
});
|
initial: { ...props.filters },
|
||||||
|
});
|
||||||
|
|
||||||
useDataTableQuerySync(() => props.filters, syncFromServer);
|
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||||
|
|
||||||
const columns = computed(() => createColumns(
|
const columns = computed(() =>
|
||||||
props.authEmployeeId,
|
createColumns(props.authEmployeeId, openEditModal, openRejectModal),
|
||||||
openEditModal,
|
);
|
||||||
openRejectModal,
|
|
||||||
));
|
|
||||||
|
|
||||||
const currentSort = computed<DataTableSort | null>(() => {
|
const currentSort = computed<DataTableSort | null>(() => {
|
||||||
if (!query.value.sort || !query.value.direction) {
|
if (!query.value.sort || !query.value.direction) {
|
||||||
@ -85,35 +86,48 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Cuti" />
|
<Head title="Cuti" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Cuti</h2>
|
||||||
Cuti
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="canSubmit && !hasPending" class="shrink-0 self-start sm:self-center" @click="openCreateModal">
|
<CreateButton
|
||||||
<Plus class="size-4" />
|
v-if="canSubmit && !hasPending"
|
||||||
Ajukan
|
@click="openCreateModal"
|
||||||
</Button>
|
label="Ajukan"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
<CardContent class="min-w-0 pt-6">
|
<CardContent class="min-w-0 pt-6">
|
||||||
<DataTable v-model:search="search" :columns="columns" :data="leaveRequests.data"
|
<DataTable
|
||||||
:pagination="pagination" :pagination-links="leaveRequests.links" :sort="currentSort"
|
v-model:search="search"
|
||||||
@sort-change="setSort" @filters-reset="resetFilters" />
|
:columns="columns"
|
||||||
|
:data="leaveRequests.data"
|
||||||
|
:pagination="pagination"
|
||||||
|
:pagination-links="leaveRequests.links"
|
||||||
|
:sort="currentSort"
|
||||||
|
@sort-change="setSort"
|
||||||
|
@filters-reset="resetFilters"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<LeaveRequestFormModal v-if="canSubmit" v-model:open="formModalOpen"
|
<LeaveRequestFormModal
|
||||||
:leave-request="editingLeaveRequest" />
|
v-if="canSubmit"
|
||||||
|
v-model:open="formModalOpen"
|
||||||
|
:leave-request="editingLeaveRequest"
|
||||||
|
/>
|
||||||
|
|
||||||
<RejectLeaveRequestModal v-if="can('leave_requests.verify')" v-model:open="rejectModalOpen"
|
<RejectLeaveRequestModal
|
||||||
:leave-request="rejectingLeaveRequest" />
|
v-if="can('leave_requests.verify')"
|
||||||
|
v-model:open="rejectModalOpen"
|
||||||
|
:leave-request="rejectingLeaveRequest"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,10 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import CuttingPosForm from './form/CuttingPosForm.vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { CuttingMaterialCartItem, CuttingProductCatalogItem, CuttingRawMaterialCatalogItem, CuttingResultCartItem } from '@/types/cutting';
|
import type {
|
||||||
|
CuttingMaterialCartItem,
|
||||||
|
CuttingProductCatalogItem,
|
||||||
|
CuttingRawMaterialCatalogItem,
|
||||||
|
CuttingResultCartItem,
|
||||||
|
} from '@/types/cutting';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import CuttingPosForm from './form/CuttingPosForm.vue';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
rawMaterialCatalog: CuttingRawMaterialCatalogItem[];
|
rawMaterialCatalog: CuttingRawMaterialCatalogItem[];
|
||||||
@ -18,19 +22,16 @@ defineProps<{
|
|||||||
<Head title="Tambah Cutting" />
|
<Head title="Tambah Cutting" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">
|
||||||
Tambah Cutting
|
Tambah Cutting
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/manage/cuttings" />
|
||||||
<Link href="/admin/manage/cuttings">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CuttingPosForm
|
<CuttingPosForm
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import CuttingPosForm from './form/CuttingPosForm.vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type {
|
import type {
|
||||||
CuttingEditItem,
|
CuttingEditItem,
|
||||||
CuttingProductCatalogItem,
|
CuttingProductCatalogItem,
|
||||||
CuttingRawMaterialCatalogItem,
|
CuttingRawMaterialCatalogItem,
|
||||||
} from '@/types/cutting';
|
} from '@/types/cutting';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import CuttingPosForm from './form/CuttingPosForm.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
cutting: CuttingEditItem;
|
cutting: CuttingEditItem;
|
||||||
@ -26,7 +25,8 @@ const initialData = computed(() => ({
|
|||||||
raw_material_name: item.raw_material_price?.raw_material?.name ?? '',
|
raw_material_name: item.raw_material_price?.raw_material?.name ?? '',
|
||||||
variant: item.raw_material_price?.variant ?? '',
|
variant: item.raw_material_price?.variant ?? '',
|
||||||
unit: item.raw_material_price?.raw_material?.unit ?? 'kilogram',
|
unit: item.raw_material_price?.raw_material?.unit ?? 'kilogram',
|
||||||
unit_abbreviation: item.raw_material_price?.raw_material?.unit_abbreviation ?? '',
|
unit_abbreviation:
|
||||||
|
item.raw_material_price?.raw_material?.unit_abbreviation ?? '',
|
||||||
stock_input: item.raw_material_price?.stock_input ?? '',
|
stock_input: item.raw_material_price?.stock_input ?? '',
|
||||||
material_usage: item.material_usage_input,
|
material_usage: item.material_usage_input,
|
||||||
remaining_material: item.remaining_material_input,
|
remaining_material: item.remaining_material_input,
|
||||||
@ -49,19 +49,14 @@ const initialData = computed(() => ({
|
|||||||
<Head title="Ubah Cutting" />
|
<Head title="Ubah Cutting" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Ubah Cutting</h2>
|
||||||
Ubah Cutting
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/manage/cuttings" />
|
||||||
<Link href="/admin/manage/cuttings">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CuttingPosForm
|
<CuttingPosForm
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
|
import { useCan } from '@/composables/useCan';
|
||||||
|
import {
|
||||||
|
useDataTableQuery,
|
||||||
|
useDataTableQuerySync,
|
||||||
|
} from '@/composables/useDataTableQuery';
|
||||||
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
|
import type { CuttingListItem, PaginatedCuttings } from '@/types/cutting';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import CuttingGroupedTable from './table/CuttingGroupedTable.vue';
|
import CuttingGroupedTable from './table/CuttingGroupedTable.vue';
|
||||||
import CuttingInProgressSection from './table/CuttingInProgressSection.vue';
|
import CuttingInProgressSection from './table/CuttingInProgressSection.vue';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
|
||||||
import { useCan } from '@/composables/useCan';
|
|
||||||
import { useDataTableQuery, useDataTableQuerySync } from '@/composables/useDataTableQuery';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
|
||||||
import type { PaginatedCuttings, CuttingListItem } from '@/types/cutting';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
cuttings: PaginatedCuttings;
|
cuttings: PaginatedCuttings;
|
||||||
@ -38,11 +40,11 @@ const tablePagination = computed(() => ({
|
|||||||
total: props.cuttings.total,
|
total: props.cuttings.total,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const firstItem = computed(() => (
|
const firstItem = computed(() =>
|
||||||
props.cuttings.data.length > 0
|
props.cuttings.data.length > 0
|
||||||
? (props.cuttings.current_page - 1) * props.cuttings.per_page + 1
|
? (props.cuttings.current_page - 1) * props.cuttings.per_page + 1
|
||||||
: 0
|
: 0,
|
||||||
));
|
);
|
||||||
|
|
||||||
watch(search, (value) => {
|
watch(search, (value) => {
|
||||||
setSearch(value);
|
setSearch(value);
|
||||||
@ -60,19 +62,17 @@ watch(
|
|||||||
<Head title="Cutting" />
|
<Head title="Cutting" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Cutting</h2>
|
||||||
Cutting
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="can('cuttings.create')" as-child class="shrink-0 self-start sm:self-center">
|
<CreateButton
|
||||||
<Link href="/admin/manage/cuttings/create">
|
v-if="can('cuttings.create')"
|
||||||
<Plus class="size-4" />
|
href="/admin/manage/cuttings/create"
|
||||||
Tambah
|
/>
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<CuttingInProgressSection
|
<CuttingInProgressSection
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
|
import type {
|
||||||
|
EnumOption,
|
||||||
|
OrderCartItem,
|
||||||
|
OrderCatalogItem,
|
||||||
|
SelectOption,
|
||||||
|
} from '@/types/order';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import OrderPosForm from './form/OrderPosForm.vue';
|
import OrderPosForm from './form/OrderPosForm.vue';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
|
||||||
import type { EnumOption, OrderCartItem, OrderCatalogItem, SelectOption } from '@/types/order';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
customers: SelectOption[];
|
customers: SelectOption[];
|
||||||
@ -28,19 +32,16 @@ function onCustomerCreated(customer: { id: number; name: string }) {
|
|||||||
<Head title="Tambah Pesanan" />
|
<Head title="Tambah Pesanan" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">
|
||||||
Tambah Pesanan
|
Tambah Pesanan
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/manage/orders" />
|
||||||
<Link href="/admin/manage/orders">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<OrderPosForm
|
<OrderPosForm
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
|
import type {
|
||||||
|
EnumOption,
|
||||||
|
OrderCatalogItem,
|
||||||
|
OrderEditItem,
|
||||||
|
SelectOption,
|
||||||
|
} from '@/types/order';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import OrderPosForm from './form/OrderPosForm.vue';
|
import OrderPosForm from './form/OrderPosForm.vue';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
|
||||||
import type { EnumOption, OrderCatalogItem, OrderEditItem, SelectOption } from '@/types/order';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
order: OrderEditItem;
|
order: OrderEditItem;
|
||||||
@ -21,7 +25,9 @@ const customers = ref([...props.customers]);
|
|||||||
|
|
||||||
const initialData = computed(() => ({
|
const initialData = computed(() => ({
|
||||||
customer_id: props.order.customer_id ? String(props.order.customer_id) : '',
|
customer_id: props.order.customer_id ? String(props.order.customer_id) : '',
|
||||||
marketing_id: props.order.marketing_id ? String(props.order.marketing_id) : '',
|
marketing_id: props.order.marketing_id
|
||||||
|
? String(props.order.marketing_id)
|
||||||
|
: '',
|
||||||
channel: props.order.channel,
|
channel: props.order.channel,
|
||||||
price_type: props.order.price_type,
|
price_type: props.order.price_type,
|
||||||
payment_type: props.order.payment_type,
|
payment_type: props.order.payment_type,
|
||||||
@ -36,7 +42,8 @@ const initialData = computed(() => ({
|
|||||||
product_name: item.product_name,
|
product_name: item.product_name,
|
||||||
variant_name: item.variant_name,
|
variant_name: item.variant_name,
|
||||||
stock_quality: item.stock_quality ?? 'good',
|
stock_quality: item.stock_quality ?? 'good',
|
||||||
stock_quality_label: item.stock_quality === 'reject' ? 'Reject' : 'Bagus',
|
stock_quality_label:
|
||||||
|
item.stock_quality === 'reject' ? 'Reject' : 'Bagus',
|
||||||
quantity: item.quantity_input,
|
quantity: item.quantity_input,
|
||||||
unit_price: item.unit_price,
|
unit_price: item.unit_price,
|
||||||
images: item.product_variant?.images ?? [],
|
images: item.product_variant?.images ?? [],
|
||||||
@ -52,22 +59,17 @@ function onCustomerCreated(customer: { id: number; name: string }) {
|
|||||||
<Head title="Ubah Pesanan" />
|
<Head title="Ubah Pesanan" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Ubah Pesanan</h2>
|
||||||
Ubah Pesanan
|
<p class="text-sm text-muted-foreground">
|
||||||
</h2>
|
|
||||||
<p class="text-muted-foreground text-sm">
|
|
||||||
{{ order.order_number }}
|
{{ order.order_number }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/manage/orders" />
|
||||||
<Link href="/admin/manage/orders">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<OrderPosForm
|
<OrderPosForm
|
||||||
|
|||||||
@ -1,21 +1,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link, usePage } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, onMounted, ref, watch } from 'vue';
|
|
||||||
import { toast } from 'vue-sonner';
|
|
||||||
import OrderGroupedTable from './table/OrderGroupedTable.vue';
|
|
||||||
import ThermalPrinterConnectButton from './form/ThermalPrinterConnectButton.vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import {
|
import {
|
||||||
useDataTableQuery,
|
useDataTableQuery,
|
||||||
useDataTableQuerySync,
|
useDataTableQuerySync,
|
||||||
} from '@/composables/useDataTableQuery';
|
} from '@/composables/useDataTableQuery';
|
||||||
import { useThermalPrinter, getPaperSizeLabel } from '@/composables/useThermalPrinter';
|
import {
|
||||||
|
getPaperSizeLabel,
|
||||||
|
useThermalPrinter,
|
||||||
|
} from '@/composables/useThermalPrinter';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { PaperSize } from '@/lib/thermal-printer/types';
|
import type { PaperSize } from '@/lib/thermal-printer/types';
|
||||||
import type { PaginatedOrders } from '@/types/order';
|
import type { PaginatedOrders } from '@/types/order';
|
||||||
|
import { Head, usePage } from '@inertiajs/vue3';
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
|
import { toast } from 'vue-sonner';
|
||||||
|
import ThermalPrinterConnectButton from './form/ThermalPrinterConnectButton.vue';
|
||||||
|
import OrderGroupedTable from './table/OrderGroupedTable.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
orders: PaginatedOrders;
|
orders: PaginatedOrders;
|
||||||
@ -92,9 +94,14 @@ onMounted(async () => {
|
|||||||
storeAddress: (page.props.address as string) ?? '',
|
storeAddress: (page.props.address as string) ?? '',
|
||||||
});
|
});
|
||||||
|
|
||||||
toast.success(`Struk ${order.order_number} dikirim ke printer ${getPaperSizeLabel(paperSize)}.`);
|
toast.success(
|
||||||
|
`Struk ${order.order_number} dikirim ke printer ${getPaperSizeLabel(paperSize)}.`,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : 'Gagal mencetak struk pesanan.';
|
const message =
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: 'Gagal mencetak struk pesanan.';
|
||||||
|
|
||||||
toast.error(message);
|
toast.error(message);
|
||||||
}
|
}
|
||||||
@ -102,31 +109,36 @@ onMounted(async () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Pesanan" />
|
<Head title="Pesanan" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">Pesanan</h2>
|
<h2 class="text-2xl font-bold tracking-tight">Pesanan</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex shrink-0 items-center gap-2 self-start sm:self-center" v-if="can('orders.create')">
|
<div
|
||||||
|
class="flex shrink-0 items-center gap-2 self-start sm:self-center"
|
||||||
|
v-if="can('orders.create')"
|
||||||
|
>
|
||||||
<ThermalPrinterConnectButton />
|
<ThermalPrinterConnectButton />
|
||||||
|
|
||||||
<Button as-child>
|
<CreateButton href="/admin/manage/orders/create" />
|
||||||
<Link href="/admin/manage/orders/create">
|
|
||||||
<Plus class="size-4" />
|
|
||||||
Tambah
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
<CardContent class="min-w-0">
|
<CardContent class="min-w-0">
|
||||||
<OrderGroupedTable v-model:search="search" :orders="orders.data" :first-item="firstItem"
|
<OrderGroupedTable
|
||||||
:pagination="tablePagination" :pagination-links="orders.links" @filters-reset="resetFilters" />
|
v-model:search="search"
|
||||||
|
:orders="orders.data"
|
||||||
|
:first-item="firstItem"
|
||||||
|
:pagination="tablePagination"
|
||||||
|
:pagination-links="orders.links"
|
||||||
|
@filters-reset="resetFilters"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link, router } from '@inertiajs/vue3';
|
import { Head, Link, router } from '@inertiajs/vue3';
|
||||||
import {
|
import {
|
||||||
ArrowLeft,
|
|
||||||
Check,
|
Check,
|
||||||
CreditCard,
|
CreditCard,
|
||||||
MapPin,
|
MapPin,
|
||||||
@ -20,7 +19,6 @@ import { toast } from 'vue-sonner';
|
|||||||
import OrderPrintButton from './form/OrderPrintButton.vue';
|
import OrderPrintButton from './form/OrderPrintButton.vue';
|
||||||
import ConfirmDialog from '@/components/ConfirmDialog.vue';
|
import ConfirmDialog from '@/components/ConfirmDialog.vue';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Separator } from '@/components/ui/separator';
|
import { Separator } from '@/components/ui/separator';
|
||||||
import {
|
import {
|
||||||
@ -258,12 +256,7 @@ const feeEntries = computed(() => {
|
|||||||
Hapus
|
Hapus
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button variant="outline" size="sm" as-child>
|
<BackButton href="/admin/manage/orders" />
|
||||||
<Link href="/admin/manage/orders">
|
|
||||||
<ArrowLeft class="size-3.5" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import PurchasePosForm from './form/PurchasePosForm.vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { PurchaseCartItem, PurchaseCatalogItem, SelectOption } from '@/types/purchase';
|
import type {
|
||||||
|
PurchaseCartItem,
|
||||||
|
PurchaseCatalogItem,
|
||||||
|
SelectOption,
|
||||||
|
} from '@/types/purchase';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import PurchasePosForm from './form/PurchasePosForm.vue';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
suppliers: SelectOption[];
|
suppliers: SelectOption[];
|
||||||
@ -17,19 +20,16 @@ defineProps<{
|
|||||||
<Head title="Tambah Belanja" />
|
<Head title="Tambah Belanja" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">
|
||||||
Tambah Belanja
|
Tambah Belanja
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/manage/purchases" />
|
||||||
<Link href="/admin/manage/purchases">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PurchasePosForm
|
<PurchasePosForm
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
|
import type {
|
||||||
|
PurchaseCatalogItem,
|
||||||
|
PurchaseEditItem,
|
||||||
|
SelectOption,
|
||||||
|
} from '@/types/purchase';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import PurchasePosForm from './form/PurchasePosForm.vue';
|
import PurchasePosForm from './form/PurchasePosForm.vue';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
|
||||||
import type { PurchaseCatalogItem, PurchaseEditItem, SelectOption } from '@/types/purchase';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
purchase: PurchaseEditItem;
|
purchase: PurchaseEditItem;
|
||||||
@ -35,19 +38,14 @@ const initialData = computed(() => ({
|
|||||||
<Head title="Ubah Belanja" />
|
<Head title="Ubah Belanja" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Ubah Belanja</h2>
|
||||||
Ubah Belanja
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/manage/purchases" />
|
||||||
<Link href="/admin/manage/purchases">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PurchasePosForm
|
<PurchasePosForm
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import PurchaseGroupedTable from './table/PurchaseGroupedTable.vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import { useDataTableQuery, useDataTableQuerySync } from '@/composables/useDataTableQuery';
|
import {
|
||||||
|
useDataTableQuery,
|
||||||
|
useDataTableQuerySync,
|
||||||
|
} from '@/composables/useDataTableQuery';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { PaginatedPurchases } from '@/types/purchase';
|
import type { PaginatedPurchases } from '@/types/purchase';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
import PurchaseGroupedTable from './table/PurchaseGroupedTable.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
purchases: PaginatedPurchases;
|
purchases: PaginatedPurchases;
|
||||||
@ -36,11 +38,11 @@ const tablePagination = computed(() => ({
|
|||||||
total: props.purchases.total,
|
total: props.purchases.total,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const firstItem = computed(() => (
|
const firstItem = computed(() =>
|
||||||
props.purchases.data.length > 0
|
props.purchases.data.length > 0
|
||||||
? (props.purchases.current_page - 1) * props.purchases.per_page + 1
|
? (props.purchases.current_page - 1) * props.purchases.per_page + 1
|
||||||
: 0
|
: 0,
|
||||||
));
|
);
|
||||||
|
|
||||||
watch(search, (value) => {
|
watch(search, (value) => {
|
||||||
setSearch(value);
|
setSearch(value);
|
||||||
@ -58,19 +60,17 @@ watch(
|
|||||||
<Head title="Belanja" />
|
<Head title="Belanja" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Belanja</h2>
|
||||||
Belanja
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="can('purchases.create')" as-child class="shrink-0 self-start sm:self-center">
|
<CreateButton
|
||||||
<Link href="/admin/manage/purchases/create">
|
v-if="can('purchases.create')"
|
||||||
<Plus class="size-4" />
|
href="/admin/manage/purchases/create"
|
||||||
Tambah
|
/>
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import { DataTable } from '@/components/data-table';
|
import { DataTable } from '@/components/data-table';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import { useDataTableQuery, useDataTableQuerySync } from '@/composables/useDataTableQuery';
|
import {
|
||||||
|
useDataTableQuery,
|
||||||
|
useDataTableQuerySync,
|
||||||
|
} from '@/composables/useDataTableQuery';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { CategoryListItem, PaginatedCategories } from '@/types/category';
|
import type { CategoryListItem, PaginatedCategories } from '@/types/category';
|
||||||
import type { DataTableSort } from '@/types/data-table';
|
import type { DataTableSort } from '@/types/data-table';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
import CategoryFormModal from './form/CategoryFormModal.vue';
|
import CategoryFormModal from './form/CategoryFormModal.vue';
|
||||||
import { createColumns } from './table/columns';
|
import { createColumns } from './table/columns';
|
||||||
|
|
||||||
@ -27,10 +29,11 @@ const search = ref(props.filters.search ?? '');
|
|||||||
const formModalOpen = ref(false);
|
const formModalOpen = ref(false);
|
||||||
const editingCategory = ref<CategoryListItem | null>(null);
|
const editingCategory = ref<CategoryListItem | null>(null);
|
||||||
|
|
||||||
const { query, setSearch, setSort, resetFilters, syncFromServer } = useDataTableQuery({
|
const { query, setSearch, setSort, resetFilters, syncFromServer } =
|
||||||
url: '/admin/master/categories',
|
useDataTableQuery({
|
||||||
initial: { ...props.filters },
|
url: '/admin/master/categories',
|
||||||
});
|
initial: { ...props.filters },
|
||||||
|
});
|
||||||
|
|
||||||
useDataTableQuerySync(() => props.filters, syncFromServer);
|
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||||
|
|
||||||
@ -77,32 +80,41 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Kategori" />
|
<Head title="Kategori" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Kategori</h2>
|
||||||
Kategori
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="can('categories.create')" class="shrink-0 self-start sm:self-center" @click="openCreateModal">
|
<CreateButton
|
||||||
<Plus class="size-4" />
|
v-if="can('categories.create')"
|
||||||
Tambah
|
@click="openCreateModal"
|
||||||
</Button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
<CardContent class="min-w-0">
|
<CardContent class="min-w-0">
|
||||||
<DataTable v-model:search="search" :columns="columns" :data="categories.data" :pagination="pagination"
|
<DataTable
|
||||||
:pagination-links="categories.links" :sort="currentSort" @sort-change="setSort"
|
v-model:search="search"
|
||||||
@filters-reset="resetFilters" />
|
:columns="columns"
|
||||||
|
:data="categories.data"
|
||||||
|
:pagination="pagination"
|
||||||
|
:pagination-links="categories.links"
|
||||||
|
:sort="currentSort"
|
||||||
|
@sort-change="setSort"
|
||||||
|
@filters-reset="resetFilters"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<CategoryFormModal v-if="can('categories.create') || can('categories.update')" v-model:open="formModalOpen"
|
<CategoryFormModal
|
||||||
:category="editingCategory" />
|
v-if="can('categories.create') || can('categories.update')"
|
||||||
|
v-model:open="formModalOpen"
|
||||||
|
:category="editingCategory"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import { DataTable } from '@/components/data-table';
|
import { DataTable } from '@/components/data-table';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import { useDataTableQuery, useDataTableQuerySync } from '@/composables/useDataTableQuery';
|
import {
|
||||||
|
useDataTableQuery,
|
||||||
|
useDataTableQuerySync,
|
||||||
|
} from '@/composables/useDataTableQuery';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { CustomerListItem, PaginatedCustomers } from '@/types/customer';
|
import type { CustomerListItem, PaginatedCustomers } from '@/types/customer';
|
||||||
import type { DataTableSort } from '@/types/data-table';
|
import type { DataTableSort } from '@/types/data-table';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
import CustomerFormModal from './form/CustomerFormModal.vue';
|
import CustomerFormModal from './form/CustomerFormModal.vue';
|
||||||
import { createColumns } from './table/columns';
|
import { createColumns } from './table/columns';
|
||||||
|
|
||||||
@ -27,10 +29,11 @@ const search = ref(props.filters.search ?? '');
|
|||||||
const formModalOpen = ref(false);
|
const formModalOpen = ref(false);
|
||||||
const editingCustomer = ref<CustomerListItem | null>(null);
|
const editingCustomer = ref<CustomerListItem | null>(null);
|
||||||
|
|
||||||
const { query, setSearch, setSort, resetFilters, syncFromServer } = useDataTableQuery({
|
const { query, setSearch, setSort, resetFilters, syncFromServer } =
|
||||||
url: '/admin/master/customers',
|
useDataTableQuery({
|
||||||
initial: { ...props.filters },
|
url: '/admin/master/customers',
|
||||||
});
|
initial: { ...props.filters },
|
||||||
|
});
|
||||||
|
|
||||||
useDataTableQuerySync(() => props.filters, syncFromServer);
|
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||||
|
|
||||||
@ -77,32 +80,41 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Customer" />
|
<Head title="Customer" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Customer</h2>
|
||||||
Customer
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="can('customers.create')" class="shrink-0 self-start sm:self-center" @click="openCreateModal">
|
<CreateButton
|
||||||
<Plus class="size-4" />
|
v-if="can('customers.create')"
|
||||||
Tambah
|
@click="openCreateModal"
|
||||||
</Button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
<CardContent class="min-w-0">
|
<CardContent class="min-w-0">
|
||||||
<DataTable v-model:search="search" :columns="columns" :data="customers.data" :pagination="pagination"
|
<DataTable
|
||||||
:pagination-links="customers.links" :sort="currentSort" @sort-change="setSort"
|
v-model:search="search"
|
||||||
@filters-reset="resetFilters" />
|
:columns="columns"
|
||||||
|
:data="customers.data"
|
||||||
|
:pagination="pagination"
|
||||||
|
:pagination-links="customers.links"
|
||||||
|
:sort="currentSort"
|
||||||
|
@sort-change="setSort"
|
||||||
|
@filters-reset="resetFilters"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<CustomerFormModal v-if="can('customers.create') || can('customers.update')" v-model:open="formModalOpen"
|
<CustomerFormModal
|
||||||
:customer="editingCustomer" />
|
v-if="can('customers.create') || can('customers.update')"
|
||||||
|
v-model:open="formModalOpen"
|
||||||
|
:customer="editingCustomer"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { CategoryOption } from '@/types/product';
|
import type { CategoryOption } from '@/types/product';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
import ProductForm from './form/ProductForm.vue';
|
import ProductForm from './form/ProductForm.vue';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@ -12,25 +11,24 @@ defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Tambah Produk" />
|
<Head title="Tambah Produk" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Tambah Produk</h2>
|
||||||
Tambah Produk
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/master/products" />
|
||||||
<Link href="/admin/master/products">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ProductForm submit-url="/admin/master/products" method="post" submit-label="Simpan" :categories="categories" />
|
<ProductForm
|
||||||
|
submit-url="/admin/master/products"
|
||||||
|
method="post"
|
||||||
|
submit-label="Simpan"
|
||||||
|
:categories="categories"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { CategoryOption, ProductListItem } from '@/types/product';
|
import type { CategoryOption, ProductListItem } from '@/types/product';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed } from 'vue';
|
||||||
import ProductForm from './form/ProductForm.vue';
|
import ProductForm from './form/ProductForm.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@ -15,7 +14,8 @@ const props = defineProps<{
|
|||||||
const initialData = computed(() => ({
|
const initialData = computed(() => ({
|
||||||
name: props.product.name ?? '',
|
name: props.product.name ?? '',
|
||||||
description: props.product.description ?? '',
|
description: props.product.description ?? '',
|
||||||
category_ids: props.product.categories?.map((category) => category.id) ?? [],
|
category_ids:
|
||||||
|
props.product.categories?.map((category) => category.id) ?? [],
|
||||||
variants: (props.product.variants ?? []).map((variant) => ({
|
variants: (props.product.variants ?? []).map((variant) => ({
|
||||||
id: variant.id,
|
id: variant.id,
|
||||||
name: variant.name,
|
name: variant.name,
|
||||||
@ -26,26 +26,25 @@ const initialData = computed(() => ({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Ubah Produk" />
|
<Head title="Ubah Produk" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Ubah Produk</h2>
|
||||||
Ubah Produk
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/master/products" />
|
||||||
<Link href="/admin/master/products">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ProductForm :submit-url="`/admin/master/products/${product.id}`" method="put" submit-label="Perbarui"
|
<ProductForm
|
||||||
:initial-data="initialData" :categories="categories" />
|
:submit-url="`/admin/master/products/${product.id}`"
|
||||||
|
method="put"
|
||||||
|
submit-label="Perbarui"
|
||||||
|
:initial-data="initialData"
|
||||||
|
:categories="categories"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,22 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import { ActiveStatus } from '@/constants/active-status';
|
|
||||||
import { StockStatus } from '@/constants/stock-status';
|
|
||||||
import {
|
import {
|
||||||
useDataTableQuery,
|
useDataTableQuery,
|
||||||
useDataTableQuerySync,
|
useDataTableQuerySync,
|
||||||
} from '@/composables/useDataTableQuery';
|
} from '@/composables/useDataTableQuery';
|
||||||
|
import { ActiveStatus } from '@/constants/active-status';
|
||||||
|
import { StockStatus } from '@/constants/stock-status';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { DataTableFilterDef } from '@/types/data-table';
|
import type { DataTableFilterDef } from '@/types/data-table';
|
||||||
import type {
|
import type { CategoryOption, PaginatedProducts } from '@/types/product';
|
||||||
CategoryOption,
|
import { Head } from '@inertiajs/vue3';
|
||||||
PaginatedProducts,
|
import { computed, ref, watch } from 'vue';
|
||||||
} from '@/types/product';
|
|
||||||
import ProductGroupedTable from './table/ProductGroupedTable.vue';
|
import ProductGroupedTable from './table/ProductGroupedTable.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@ -49,7 +45,10 @@ const filterDefs = computed<DataTableFilterDef[]>(() => [
|
|||||||
key: 'category_id',
|
key: 'category_id',
|
||||||
label: 'Kategori',
|
label: 'Kategori',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: props.categories.map((c) => ({ value: String(c.value), label: c.label })),
|
options: props.categories.map((c) => ({
|
||||||
|
value: String(c.value),
|
||||||
|
label: c.label,
|
||||||
|
})),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'is_active',
|
key: 'is_active',
|
||||||
@ -103,28 +102,35 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Produk" />
|
<Head title="Produk" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">Produk</h2>
|
<h2 class="text-2xl font-bold tracking-tight">Produk</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="can('products.create')" as-child class="shrink-0 self-start sm:self-center">
|
<CreateButton
|
||||||
<Link href="/admin/master/products/create">
|
v-if="can('products.create')"
|
||||||
<Plus class="size-4" />
|
href="/admin/master/products/create"
|
||||||
Tambah
|
/>
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
<CardContent class="min-w-0">
|
<CardContent class="min-w-0">
|
||||||
<ProductGroupedTable v-model:search="search" :products="products.data" :first-item="firstItem"
|
<ProductGroupedTable
|
||||||
:pagination="pagination" :pagination-links="products.links" :filter-defs="filterDefs"
|
v-model:search="search"
|
||||||
:filter-values="filterValues" @filter-change="setFilter" @filters-reset="resetFilters" />
|
:products="products.data"
|
||||||
|
:first-item="firstItem"
|
||||||
|
:pagination="pagination"
|
||||||
|
:pagination-links="products.links"
|
||||||
|
:filter-defs="filterDefs"
|
||||||
|
:filter-values="filterValues"
|
||||||
|
@filter-change="setFilter"
|
||||||
|
@filters-reset="resetFilters"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import { Head, Link } from '@inertiajs/vue3';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { EnumOption } from '@/types/raw-material';
|
import type { EnumOption } from '@/types/raw-material';
|
||||||
import RawMaterialForm from './form/RawMaterialForm.vue';
|
import RawMaterialForm from './form/RawMaterialForm.vue';
|
||||||
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
units: EnumOption[];
|
units: EnumOption[];
|
||||||
@ -23,12 +22,7 @@ defineProps<{
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/master/raw-materials" />
|
||||||
<Link href="/admin/master/raw-materials">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<RawMaterialForm submit-url="/admin/master/raw-materials" method="post" submit-label="Simpan" :units="units" />
|
<RawMaterialForm submit-url="/admin/master/raw-materials" method="post" submit-label="Simpan" :units="units" />
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { EnumOption, RawMaterialListItem } from '@/types/raw-material';
|
import type { EnumOption, RawMaterialListItem } from '@/types/raw-material';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed } from 'vue';
|
||||||
import RawMaterialForm from './form/RawMaterialForm.vue';
|
import RawMaterialForm from './form/RawMaterialForm.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@ -26,26 +25,27 @@ const initialData = computed(() => ({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Ubah Bahan Baku" />
|
<Head title="Ubah Bahan Baku" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">
|
||||||
Ubah Bahan Baku
|
Ubah Bahan Baku
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/master/raw-materials" />
|
||||||
<Link href="/admin/master/raw-materials">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<RawMaterialForm :submit-url="`/admin/master/raw-materials/${rawMaterial.id}`" method="put"
|
<RawMaterialForm
|
||||||
submit-label="Perbarui" :initial-data="initialData" :units="units" />
|
:submit-url="`/admin/master/raw-materials/${rawMaterial.id}`"
|
||||||
|
method="put"
|
||||||
|
submit-label="Perbarui"
|
||||||
|
:initial-data="initialData"
|
||||||
|
:units="units"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,19 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import { ActiveStatus } from '@/constants/active-status';
|
|
||||||
import { StockStatus } from '@/constants/stock-status';
|
|
||||||
import {
|
import {
|
||||||
useDataTableQuery,
|
useDataTableQuery,
|
||||||
useDataTableQuerySync,
|
useDataTableQuerySync,
|
||||||
} from '@/composables/useDataTableQuery';
|
} from '@/composables/useDataTableQuery';
|
||||||
|
import { ActiveStatus } from '@/constants/active-status';
|
||||||
|
import { StockStatus } from '@/constants/stock-status';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { DataTableFilterDef } from '@/types/data-table';
|
import type { DataTableFilterDef } from '@/types/data-table';
|
||||||
import type { PaginatedRawMaterials } from '@/types/raw-material';
|
import type { PaginatedRawMaterials } from '@/types/raw-material';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
import RawMaterialGroupedTable from './table/RawMaterialGroupedTable.vue';
|
import RawMaterialGroupedTable from './table/RawMaterialGroupedTable.vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@ -75,7 +74,7 @@ const pagination = computed(() => ({
|
|||||||
const firstItem = computed(() =>
|
const firstItem = computed(() =>
|
||||||
props.rawMaterials.data.length > 0
|
props.rawMaterials.data.length > 0
|
||||||
? (props.rawMaterials.current_page - 1) * props.rawMaterials.per_page +
|
? (props.rawMaterials.current_page - 1) * props.rawMaterials.per_page +
|
||||||
1
|
1
|
||||||
: 0,
|
: 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -92,28 +91,35 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Bahan Baku" />
|
<Head title="Bahan Baku" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">Bahan Baku</h2>
|
<h2 class="text-2xl font-bold tracking-tight">Bahan Baku</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="can('raw_materials.create')" as-child class="shrink-0 self-start sm:self-center">
|
<CreateButton
|
||||||
<Link href="/admin/master/raw-materials/create">
|
v-if="can('raw_materials.create')"
|
||||||
<Plus class="size-4" />
|
href="/admin/master/raw-materials/create"
|
||||||
Tambah
|
/>
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
<CardContent class="min-w-0">
|
<CardContent class="min-w-0">
|
||||||
<RawMaterialGroupedTable v-model:search="search" :materials="rawMaterials.data" :first-item="firstItem"
|
<RawMaterialGroupedTable
|
||||||
:pagination="pagination" :pagination-links="rawMaterials.links" :filter-defs="filterDefs"
|
v-model:search="search"
|
||||||
:filter-values="filterValues" @filter-change="setFilter" @filters-reset="resetFilters" />
|
:materials="rawMaterials.data"
|
||||||
|
:first-item="firstItem"
|
||||||
|
:pagination="pagination"
|
||||||
|
:pagination-links="rawMaterials.links"
|
||||||
|
:filter-defs="filterDefs"
|
||||||
|
:filter-values="filterValues"
|
||||||
|
@filter-change="setFilter"
|
||||||
|
@filters-reset="resetFilters"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import { DataTable } from '@/components/data-table';
|
import { DataTable } from '@/components/data-table';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import { useDataTableQuery, useDataTableQuerySync } from '@/composables/useDataTableQuery';
|
import {
|
||||||
|
useDataTableQuery,
|
||||||
|
useDataTableQuerySync,
|
||||||
|
} from '@/composables/useDataTableQuery';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { DataTableSort } from '@/types/data-table';
|
import type { DataTableSort } from '@/types/data-table';
|
||||||
import type { SupplierListItem, PaginatedSuppliers } from '@/types/supplier';
|
import type { PaginatedSuppliers, SupplierListItem } from '@/types/supplier';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
import SupplierFormModal from './form/SupplierFormModal.vue';
|
import SupplierFormModal from './form/SupplierFormModal.vue';
|
||||||
import { createColumns } from './table/columns';
|
import { createColumns } from './table/columns';
|
||||||
|
|
||||||
@ -27,10 +29,11 @@ const search = ref(props.filters.search ?? '');
|
|||||||
const formModalOpen = ref(false);
|
const formModalOpen = ref(false);
|
||||||
const editingSupplier = ref<SupplierListItem | null>(null);
|
const editingSupplier = ref<SupplierListItem | null>(null);
|
||||||
|
|
||||||
const { query, setSearch, setSort, resetFilters, syncFromServer } = useDataTableQuery({
|
const { query, setSearch, setSort, resetFilters, syncFromServer } =
|
||||||
url: '/admin/master/suppliers',
|
useDataTableQuery({
|
||||||
initial: { ...props.filters },
|
url: '/admin/master/suppliers',
|
||||||
});
|
initial: { ...props.filters },
|
||||||
|
});
|
||||||
|
|
||||||
useDataTableQuerySync(() => props.filters, syncFromServer);
|
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||||
|
|
||||||
@ -77,32 +80,41 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Supplier" />
|
<Head title="Supplier" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Supplier</h2>
|
||||||
Supplier
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="can('suppliers.create')" class="shrink-0 self-start sm:self-center" @click="openCreateModal">
|
<CreateButton
|
||||||
<Plus class="size-4" />
|
v-if="can('suppliers.create')"
|
||||||
Tambah
|
@click="openCreateModal"
|
||||||
</Button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
<CardContent class="min-w-0">
|
<CardContent class="min-w-0">
|
||||||
<DataTable v-model:search="search" :columns="columns" :data="suppliers.data" :pagination="pagination"
|
<DataTable
|
||||||
:pagination-links="suppliers.links" :sort="currentSort" @sort-change="setSort"
|
v-model:search="search"
|
||||||
@filters-reset="resetFilters" />
|
:columns="columns"
|
||||||
|
:data="suppliers.data"
|
||||||
|
:pagination="pagination"
|
||||||
|
:pagination-links="suppliers.links"
|
||||||
|
:sort="currentSort"
|
||||||
|
@sort-change="setSort"
|
||||||
|
@filters-reset="resetFilters"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<SupplierFormModal v-if="can('suppliers.create') || can('suppliers.update')" v-model:open="formModalOpen"
|
<SupplierFormModal
|
||||||
:supplier="editingSupplier" />
|
v-if="can('suppliers.create') || can('suppliers.update')"
|
||||||
|
v-model:open="formModalOpen"
|
||||||
|
:supplier="editingSupplier"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Eye } from '@lucide/vue';
|
import { RowDetailAction } from '@/components/button';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import Tooltip from '@/components/ui/tooltip/Tooltip.vue';
|
|
||||||
import TooltipContent from '@/components/ui/tooltip/TooltipContent.vue';
|
|
||||||
import TooltipTrigger from '@/components/ui/tooltip/TooltipTrigger.vue';
|
|
||||||
import type { ActivityLogListItem } from '@/types/activity-log';
|
import type { ActivityLogListItem } from '@/types/activity-log';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
@ -16,13 +12,5 @@ const emit = defineEmits<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Tooltip>
|
<RowDetailAction tooltip="Lihat" @click="emit('view')" />
|
||||||
<TooltipTrigger as-child>
|
|
||||||
<Button variant="ghost" size="icon" class="size-8" @click="emit('view')">
|
|
||||||
<Eye class="size-4" />
|
|
||||||
<span class="sr-only">Lihat</span>
|
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Lihat</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import { Head, Link } from '@inertiajs/vue3';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import RoleForm from './form/RoleForm.vue';
|
import RoleForm from './form/RoleForm.vue';
|
||||||
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
|
|
||||||
interface PermissionOption {
|
interface PermissionOption {
|
||||||
value: string;
|
value: string;
|
||||||
@ -28,12 +27,7 @@ defineProps<{
|
|||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/system/roles" />
|
||||||
<Link href="/admin/system/roles">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<RoleForm submit-url="/admin/system/roles" method="post" submit-label="Simpan" :permissions="permissions" />
|
<RoleForm submit-url="/admin/system/roles" method="post" submit-label="Simpan" :permissions="permissions" />
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import BackButton from '@/components/button/BackButton.vue';
|
||||||
import { ArrowLeft } from '@lucide/vue';
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
import RoleForm from './form/RoleForm.vue';
|
import RoleForm from './form/RoleForm.vue';
|
||||||
|
|
||||||
interface PermissionOption {
|
interface PermissionOption {
|
||||||
@ -24,26 +23,25 @@ defineProps<{
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Ubah Role" />
|
<Head title="Ubah Role" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">Ubah Role</h2>
|
||||||
Ubah Role
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button variant="outline" as-child class="shrink-0 self-start sm:self-center">
|
<BackButton href="/admin/system/roles" />
|
||||||
<Link href="/admin/system/roles">
|
|
||||||
<ArrowLeft class="size-4" />
|
|
||||||
Kembali
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<RoleForm :submit-url="`/admin/system/roles/${role.id}`" method="put" submit-label="Perbarui"
|
<RoleForm
|
||||||
:initial-data="role" :permissions="permissions" />
|
:submit-url="`/admin/system/roles/${role.id}`"
|
||||||
|
method="put"
|
||||||
|
submit-label="Perbarui"
|
||||||
|
:initial-data="role"
|
||||||
|
:permissions="permissions"
|
||||||
|
/>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
import CreateButton from '@/components/button/CreateButton.vue';
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import { DataTable } from '@/components/data-table';
|
import { DataTable } from '@/components/data-table';
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useCan } from '@/composables/useCan';
|
import { useCan } from '@/composables/useCan';
|
||||||
import {
|
import {
|
||||||
@ -12,8 +9,10 @@ import {
|
|||||||
} from '@/composables/useDataTableQuery';
|
} from '@/composables/useDataTableQuery';
|
||||||
import AdminLayout from '@/layouts/AdminLayout.vue';
|
import AdminLayout from '@/layouts/AdminLayout.vue';
|
||||||
import type { DataTableSort } from '@/types/data-table';
|
import type { DataTableSort } from '@/types/data-table';
|
||||||
import { createColumns } from './table/columns';
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
import type { RoleListItem } from './table/columns';
|
import type { RoleListItem } from './table/columns';
|
||||||
|
import { createColumns } from './table/columns';
|
||||||
|
|
||||||
interface PaginatedRoles {
|
interface PaginatedRoles {
|
||||||
current_page: number;
|
current_page: number;
|
||||||
@ -36,10 +35,11 @@ const props = defineProps<{
|
|||||||
const { can } = useCan();
|
const { can } = useCan();
|
||||||
const search = ref(props.filters.search ?? '');
|
const search = ref(props.filters.search ?? '');
|
||||||
|
|
||||||
const { query, setSearch, setSort, resetFilters, syncFromServer } = useDataTableQuery({
|
const { query, setSearch, setSort, resetFilters, syncFromServer } =
|
||||||
url: '/admin/system/roles',
|
useDataTableQuery({
|
||||||
initial: { ...props.filters },
|
url: '/admin/system/roles',
|
||||||
});
|
initial: { ...props.filters },
|
||||||
|
});
|
||||||
|
|
||||||
useDataTableQuerySync(() => props.filters, syncFromServer);
|
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||||
|
|
||||||
@ -76,30 +76,36 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<Head title="Role & Permission" />
|
<Head title="Role & Permission" />
|
||||||
|
|
||||||
<AdminLayout>
|
<AdminLayout>
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
<div
|
||||||
|
class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<h2 class="text-2xl font-bold tracking-tight">
|
<h2 class="text-2xl font-bold tracking-tight">
|
||||||
Role & Permission
|
Role & Permission
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button v-if="can('roles.create')" class="shrink-0 self-start sm:self-center" as-child>
|
<CreateButton
|
||||||
<Link href="/admin/system/roles/create">
|
v-if="can('roles.create')"
|
||||||
<Plus class="size-4" />
|
href="/admin/system/roles/create"
|
||||||
Tambah
|
/>
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Card class="min-w-0">
|
<Card class="min-w-0">
|
||||||
<CardContent class="min-w-0">
|
<CardContent class="min-w-0">
|
||||||
<DataTable v-model:search="search" :columns="columns" :data="roles.data" :pagination="pagination"
|
<DataTable
|
||||||
:pagination-links="roles.links" :sort="currentSort" @sort-change="setSort"
|
v-model:search="search"
|
||||||
@filters-reset="resetFilters" />
|
:columns="columns"
|
||||||
|
:data="roles.data"
|
||||||
|
:pagination="pagination"
|
||||||
|
:pagination-links="roles.links"
|
||||||
|
:sort="currentSort"
|
||||||
|
@sort-change="setSort"
|
||||||
|
@filters-reset="resetFilters"
|
||||||
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</AdminLayout>
|
</AdminLayout>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user