feat: enhance order management with additional filters and pagination support
- Added channel, status, and payment type filters to the order index functionality. - Updated OrderService to handle new filter parameters in pagination queries. - Introduced filter definitions and values in the OrderGroupedTable component for improved data filtering. - Enhanced the frontend to support dynamic filtering options for orders based on the new criteria.
This commit is contained in:
parent
7dec701770
commit
1f9aba6ba5
@ -30,9 +30,17 @@ public function index(Request $request): Response
|
|||||||
{
|
{
|
||||||
$tableQuery = $this->parseDataTableQuery($request);
|
$tableQuery = $this->parseDataTableQuery($request);
|
||||||
|
|
||||||
|
$tableQuery['channel'] = $request->string('channel')->toString();
|
||||||
|
$tableQuery['status'] = $request->string('status')->toString();
|
||||||
|
$tableQuery['payment_type'] = $request->string('payment_type')->toString();
|
||||||
|
|
||||||
return Inertia::render('admin/manage/orders/Index', [
|
return Inertia::render('admin/manage/orders/Index', [
|
||||||
'orders' => $this->orderService->paginateForIndex($tableQuery, $request->user()),
|
'orders' => $this->orderService->paginateForIndex($tableQuery, $request->user()),
|
||||||
'filters' => $this->dataTableFilters($tableQuery),
|
'filters' => $this->dataTableFilters($tableQuery, [
|
||||||
|
'channel' => $tableQuery['channel'],
|
||||||
|
'status' => $tableQuery['status'],
|
||||||
|
'payment_type' => $tableQuery['payment_type'],
|
||||||
|
]),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -84,7 +84,10 @@ public function paginateForIndex(array $tableQuery, User $user): LengthAwarePagi
|
|||||||
->orWhereHas('product', fn (Builder $query) => $query->where('name', 'like', "%{$search}%"));
|
->orWhereHas('product', fn (Builder $query) => $query->where('name', 'like', "%{$search}%"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
|
->when(($tableQuery['channel'] ?? '') !== '', fn (Builder $query) => $query->where('channel', $tableQuery['channel']))
|
||||||
|
->when(($tableQuery['status'] ?? '') !== '', fn (Builder $query) => $query->where('status', $tableQuery['status']))
|
||||||
|
->when(($tableQuery['payment_type'] ?? '') !== '', fn (Builder $query) => $query->where('payment_type', $tableQuery['payment_type']));
|
||||||
|
|
||||||
$this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']);
|
$this->applySorting($query, $tableQuery['sort'], $tableQuery['direction']);
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
export const OrderChannel = {
|
export const OrderChannel = {
|
||||||
|
STORE: 'store',
|
||||||
TIKTOK: 'tiktok',
|
TIKTOK: 'tiktok',
|
||||||
SHOPEE: 'shopee',
|
SHOPEE: 'shopee',
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
@ -14,9 +14,13 @@ import {
|
|||||||
getPaperSizeLabel,
|
getPaperSizeLabel,
|
||||||
useThermalPrinter,
|
useThermalPrinter,
|
||||||
} from '@/composables/useThermalPrinter';
|
} from '@/composables/useThermalPrinter';
|
||||||
|
import { OrderChannel } from '@/constants/order-channel';
|
||||||
|
import { OrderPaymentType } from '@/constants/order-payment-type';
|
||||||
|
import { OrderStatus } from '@/constants/order-status';
|
||||||
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 { index, create } from '@/routes/admin/manage/orders';
|
import { index, create } from '@/routes/admin/manage/orders';
|
||||||
|
import type { DataTableFilterDef } from '@/types/data-table';
|
||||||
import type { PaginatedOrders } from '@/types/order';
|
import type { PaginatedOrders } from '@/types/order';
|
||||||
import OrderGroupedTable from './table/OrderGroupedTable.vue';
|
import OrderGroupedTable from './table/OrderGroupedTable.vue';
|
||||||
|
|
||||||
@ -26,6 +30,9 @@ const props = defineProps<{
|
|||||||
search: string;
|
search: string;
|
||||||
sort?: string;
|
sort?: string;
|
||||||
direction?: 'asc' | 'desc';
|
direction?: 'asc' | 'desc';
|
||||||
|
channel?: string;
|
||||||
|
status?: string;
|
||||||
|
payment_type?: string;
|
||||||
};
|
};
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
@ -34,13 +41,55 @@ const page = usePage();
|
|||||||
const { isConnected, printOrderReceipt, tryReconnect } = useThermalPrinter();
|
const { isConnected, printOrderReceipt, tryReconnect } = useThermalPrinter();
|
||||||
const search = ref(props.filters.search ?? '');
|
const search = ref(props.filters.search ?? '');
|
||||||
|
|
||||||
const { setSearch, resetFilters, syncFromServer } = useDataTableQuery({
|
const { query, setSearch, setFilter, resetFilters, syncFromServer } = useDataTableQuery({
|
||||||
url: index.url(),
|
url: index.url(),
|
||||||
initial: { ...props.filters },
|
initial: { ...props.filters },
|
||||||
|
filterKeys: ['channel', 'status', 'payment_type'],
|
||||||
});
|
});
|
||||||
|
|
||||||
useDataTableQuerySync(() => props.filters, syncFromServer);
|
useDataTableQuerySync(() => props.filters, syncFromServer);
|
||||||
|
|
||||||
|
const filterDefs = computed<DataTableFilterDef[]>(() => [
|
||||||
|
{
|
||||||
|
key: 'channel',
|
||||||
|
label: 'Channel',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ value: OrderChannel.STORE, label: 'Toko' },
|
||||||
|
{ value: OrderChannel.SHOPEE, label: 'Shopee' },
|
||||||
|
{ value: OrderChannel.TIKTOK, label: 'TikTok' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'status',
|
||||||
|
label: 'Status',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ value: OrderStatus.PENDING, label: 'Menunggu' },
|
||||||
|
{ value: OrderStatus.PROCESSING, label: 'Diproses' },
|
||||||
|
{ value: OrderStatus.COMPLETED, label: 'Selesai' },
|
||||||
|
{ value: OrderStatus.CANCELLED, label: 'Dibatalkan' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'payment_type',
|
||||||
|
label: 'Tipe Pembayaran',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ value: OrderPaymentType.CASH, label: 'Cash' },
|
||||||
|
{ value: OrderPaymentType.TRANSFER, label: 'Transfer' },
|
||||||
|
{ value: OrderPaymentType.QRIS, label: 'QRIS' },
|
||||||
|
{ value: OrderPaymentType.MARKETPLACE, label: 'Marketplace' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const filterValues = computed(() => ({
|
||||||
|
channel: query.value.channel ?? '',
|
||||||
|
status: query.value.status ?? '',
|
||||||
|
payment_type: query.value.payment_type ?? '',
|
||||||
|
}));
|
||||||
|
|
||||||
const tablePagination = computed(() => ({
|
const tablePagination = computed(() => ({
|
||||||
currentPage: props.orders.current_page,
|
currentPage: props.orders.current_page,
|
||||||
perPage: props.orders.per_page,
|
perPage: props.orders.per_page,
|
||||||
@ -138,6 +187,9 @@ onMounted(async () => {
|
|||||||
:first-item="firstItem"
|
:first-item="firstItem"
|
||||||
:pagination="tablePagination"
|
:pagination="tablePagination"
|
||||||
:pagination-links="orders.links"
|
:pagination-links="orders.links"
|
||||||
|
:filter-defs="filterDefs"
|
||||||
|
:filter-values="filterValues"
|
||||||
|
@filter-change="setFilter"
|
||||||
@filters-reset="resetFilters"
|
@filters-reset="resetFilters"
|
||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { usePaginationSummary } from '@/composables/usePaginationSummary';
|
|||||||
import { orderStatusBadgeVariant } from '@/constants/order-status';
|
import { orderStatusBadgeVariant } from '@/constants/order-status';
|
||||||
import { groupedTableRowNumber } from '@/lib/grouped-table';
|
import { groupedTableRowNumber } from '@/lib/grouped-table';
|
||||||
import type {
|
import type {
|
||||||
|
DataTableFilterDef,
|
||||||
DataTablePagination,
|
DataTablePagination,
|
||||||
DataTablePaginationLink,
|
DataTablePaginationLink,
|
||||||
} from '@/types/data-table';
|
} from '@/types/data-table';
|
||||||
@ -32,11 +33,14 @@ const props = defineProps<{
|
|||||||
firstItem?: number;
|
firstItem?: number;
|
||||||
pagination?: DataTablePagination;
|
pagination?: DataTablePagination;
|
||||||
paginationLinks?: DataTablePaginationLink[];
|
paginationLinks?: DataTablePaginationLink[];
|
||||||
|
filterDefs?: DataTableFilterDef[];
|
||||||
|
filterValues?: Record<string, string>;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const search = defineModel<string>('search', { default: '' });
|
const search = defineModel<string>('search', { default: '' });
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
'filter-change': [key: string, value: string];
|
||||||
'filters-reset': [];
|
'filters-reset': [];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
@ -50,7 +54,8 @@ function rowNumber(index: number): number {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<DataTableToolbar v-model:search="search" @filters-reset="emit('filters-reset')" />
|
<DataTableToolbar v-model:search="search" :filter-defs="filterDefs" :filter-values="filterValues"
|
||||||
|
@filter-change="(key, value) => emit('filter-change', key, value)" @filters-reset="emit('filters-reset')" />
|
||||||
|
|
||||||
<div v-if="orders.length" class="space-y-4">
|
<div v-if="orders.length" class="space-y-4">
|
||||||
<div v-for="(order, index) in orders" :key="order.id" class="overflow-hidden rounded-md border">
|
<div v-for="(order, index) in orders" :key="order.id" class="overflow-hidden rounded-md border">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user