refactor: restrict marketing role permissions and add employee advances and payroll access
This commit is contained in:
parent
2dab1efeae
commit
69d2212d64
@ -201,8 +201,6 @@ public function permissions(): array
|
|||||||
self::MARKETING => [
|
self::MARKETING => [
|
||||||
Permission::DASHBOARD_VIEW,
|
Permission::DASHBOARD_VIEW,
|
||||||
|
|
||||||
Permission::EMPLOYEES_VIEW,
|
|
||||||
|
|
||||||
Permission::ATTENDANCES_VIEW,
|
Permission::ATTENDANCES_VIEW,
|
||||||
Permission::ATTENDANCES_CREATE,
|
Permission::ATTENDANCES_CREATE,
|
||||||
|
|
||||||
@ -212,28 +210,20 @@ public function permissions(): array
|
|||||||
Permission::LEAVE_REQUESTS_DELETE,
|
Permission::LEAVE_REQUESTS_DELETE,
|
||||||
|
|
||||||
Permission::CATEGORIES_VIEW,
|
Permission::CATEGORIES_VIEW,
|
||||||
Permission::CATEGORIES_CREATE,
|
|
||||||
Permission::CATEGORIES_UPDATE,
|
|
||||||
Permission::CATEGORIES_DELETE,
|
|
||||||
|
|
||||||
Permission::SUPPLIERS_VIEW,
|
|
||||||
Permission::SUPPLIERS_CREATE,
|
|
||||||
Permission::SUPPLIERS_UPDATE,
|
|
||||||
Permission::SUPPLIERS_DELETE,
|
|
||||||
|
|
||||||
Permission::CUSTOMERS_VIEW,
|
Permission::CUSTOMERS_VIEW,
|
||||||
Permission::CUSTOMERS_CREATE,
|
|
||||||
Permission::CUSTOMERS_UPDATE,
|
|
||||||
Permission::CUSTOMERS_DELETE,
|
|
||||||
|
|
||||||
Permission::PRODUCTS_VIEW,
|
Permission::PRODUCTS_VIEW,
|
||||||
|
|
||||||
Permission::ORDERS_VIEW,
|
Permission::ORDERS_VIEW,
|
||||||
Permission::ORDERS_CREATE,
|
|
||||||
Permission::ORDERS_UPDATE,
|
Permission::EMPLOYEE_ADVANCES_VIEW,
|
||||||
Permission::ORDERS_SEND,
|
Permission::EMPLOYEE_ADVANCES_CREATE,
|
||||||
Permission::ORDERS_COMPLETE,
|
Permission::EMPLOYEE_ADVANCES_UPDATE,
|
||||||
Permission::ORDERS_CANCEL,
|
Permission::EMPLOYEE_ADVANCES_DELETE,
|
||||||
|
Permission::EMPLOYEE_ADVANCES_PAY,
|
||||||
|
|
||||||
|
Permission::PAYROLL_VIEW,
|
||||||
],
|
],
|
||||||
|
|
||||||
self::NON_OPERATOR => [
|
self::NON_OPERATOR => [
|
||||||
|
|||||||
@ -41,6 +41,7 @@ public function create(Request $request): Response
|
|||||||
|
|
||||||
return Inertia::render('admin/manage/orders/Create', [
|
return Inertia::render('admin/manage/orders/Create', [
|
||||||
'customers' => $this->orderService->customerOptions(),
|
'customers' => $this->orderService->customerOptions(),
|
||||||
|
'marketings' => $this->orderService->marketingOptions(),
|
||||||
'catalog' => $this->orderService->catalogItems(user: $user),
|
'catalog' => $this->orderService->catalogItems(user: $user),
|
||||||
'channels' => OrderChannel::selectOptions(),
|
'channels' => OrderChannel::selectOptions(),
|
||||||
'storePriceTypes' => $this->orderService->storePriceTypeOptions(),
|
'storePriceTypes' => $this->orderService->storePriceTypeOptions(),
|
||||||
@ -68,6 +69,7 @@ public function edit(Order $order): Response|RedirectResponse
|
|||||||
return Inertia::render('admin/manage/orders/Edit', [
|
return Inertia::render('admin/manage/orders/Edit', [
|
||||||
'order' => $this->orderService->findForEdit($order),
|
'order' => $this->orderService->findForEdit($order),
|
||||||
'customers' => $this->orderService->customerOptions(),
|
'customers' => $this->orderService->customerOptions(),
|
||||||
|
'marketings' => $this->orderService->marketingOptions(),
|
||||||
'catalog' => $this->orderService->catalogItems($order),
|
'catalog' => $this->orderService->catalogItems($order),
|
||||||
'channels' => OrderChannel::selectOptions(),
|
'channels' => OrderChannel::selectOptions(),
|
||||||
'storePriceTypes' => $this->orderService->storePriceTypeOptions(),
|
'storePriceTypes' => $this->orderService->storePriceTypeOptions(),
|
||||||
|
|||||||
@ -28,6 +28,7 @@ public function rules(): array
|
|||||||
{
|
{
|
||||||
$rules = [
|
$rules = [
|
||||||
'customer_id' => ['nullable', 'integer', Rule::exists('customers', 'id')->whereNull('deleted_at')],
|
'customer_id' => ['nullable', 'integer', Rule::exists('customers', 'id')->whereNull('deleted_at')],
|
||||||
|
'marketing_id' => ['nullable', 'integer', Rule::exists('users', 'id')->whereNull('deleted_at')],
|
||||||
'channel' => ['required', Rule::enum(OrderChannel::class)],
|
'channel' => ['required', Rule::enum(OrderChannel::class)],
|
||||||
'price_type' => ['required', Rule::enum(PriceType::class)],
|
'price_type' => ['required', Rule::enum(PriceType::class)],
|
||||||
'discount' => ['nullable', 'integer', 'min:0'],
|
'discount' => ['nullable', 'integer', 'min:0'],
|
||||||
@ -54,6 +55,7 @@ public function attributes(): array
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'customer_id' => 'pelanggan',
|
'customer_id' => 'pelanggan',
|
||||||
|
'marketing_id' => 'marketing',
|
||||||
'channel' => 'channel',
|
'channel' => 'channel',
|
||||||
'price_type' => 'tipe harga',
|
'price_type' => 'tipe harga',
|
||||||
'discount' => 'diskon',
|
'discount' => 'diskon',
|
||||||
|
|||||||
@ -54,6 +54,11 @@ public function createdBy(): BelongsTo
|
|||||||
return $this->belongsTo(User::class, 'created_by_id');
|
return $this->belongsTo(User::class, 'created_by_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function marketing(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'marketing_id');
|
||||||
|
}
|
||||||
|
|
||||||
public function customer(): BelongsTo
|
public function customer(): BelongsTo
|
||||||
{
|
{
|
||||||
return $this->belongsTo(Customer::class);
|
return $this->belongsTo(Customer::class);
|
||||||
|
|||||||
@ -41,6 +41,7 @@ public function paginateForIndex(array $tableQuery, User $user): LengthAwarePagi
|
|||||||
'items.productVariant.product:id,name',
|
'items.productVariant.product:id,name',
|
||||||
'items.productVariant:id,product_id,name',
|
'items.productVariant:id,product_id,name',
|
||||||
])
|
])
|
||||||
|
->when($user->hasRole('marketing'), fn (Builder $query) => $query->where('marketing_id', $user->id))
|
||||||
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
||||||
$search = $tableQuery['search'];
|
$search = $tableQuery['search'];
|
||||||
$query->where(function (Builder $query) use ($search): void {
|
$query->where(function (Builder $query) use ($search): void {
|
||||||
@ -87,6 +88,24 @@ public function customerOptions(): array
|
|||||||
->all();
|
->all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<array{value: int, label: string}>
|
||||||
|
*/
|
||||||
|
public function marketingOptions(): array
|
||||||
|
{
|
||||||
|
return User::query()
|
||||||
|
->active()
|
||||||
|
->whereHas('roles', fn ($q) => $q->where('name', 'marketing'))
|
||||||
|
->with('profile:user_id,full_name')
|
||||||
|
->orderBy('username')
|
||||||
|
->get(['id', 'username'])
|
||||||
|
->map(fn (User $user) => [
|
||||||
|
'value' => $user->id,
|
||||||
|
'label' => $user->profile?->full_name ?? $user->username,
|
||||||
|
])
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return list<array{value: string, label: string}>
|
* @return list<array{value: string, label: string}>
|
||||||
*/
|
*/
|
||||||
@ -301,6 +320,7 @@ public function create(array $validated, User $user): Order
|
|||||||
|
|
||||||
$order = Order::create([
|
$order = Order::create([
|
||||||
'customer_id' => $validated['customer_id'] ?? null,
|
'customer_id' => $validated['customer_id'] ?? null,
|
||||||
|
'marketing_id' => $validated['marketing_id'] ?? null,
|
||||||
'channel' => $channel,
|
'channel' => $channel,
|
||||||
'price_type' => $priceType,
|
'price_type' => $priceType,
|
||||||
'status' => OrderStatus::PENDING,
|
'status' => OrderStatus::PENDING,
|
||||||
@ -374,6 +394,7 @@ public function update(Order $order, array $validated): void
|
|||||||
$channel = OrderChannel::from($validated['channel']);
|
$channel = OrderChannel::from($validated['channel']);
|
||||||
|
|
||||||
$order->customer_id = $validated['customer_id'] ?? null;
|
$order->customer_id = $validated['customer_id'] ?? null;
|
||||||
|
$order->marketing_id = $validated['marketing_id'] ?? null;
|
||||||
$order->channel = $channel;
|
$order->channel = $channel;
|
||||||
$order->price_type = $priceType;
|
$order->price_type = $priceType;
|
||||||
$order->subtotal = $subtotal;
|
$order->subtotal = $subtotal;
|
||||||
|
|||||||
@ -15,6 +15,7 @@ public function up(): void
|
|||||||
$table->id();
|
$table->id();
|
||||||
|
|
||||||
$table->foreignId('customer_id')->nullable()->constrained()->nullOnDelete();
|
$table->foreignId('customer_id')->nullable()->constrained()->nullOnDelete();
|
||||||
|
$table->foreignId('marketing_id')->nullable()->constrained('users')->nullOnDelete();
|
||||||
|
|
||||||
$table->string('order_number', 30)->unique();
|
$table->string('order_number', 30)->unique();
|
||||||
$table->enum('channel', array_column(OrderChannel::cases(), 'value'));
|
$table->enum('channel', array_column(OrderChannel::cases(), 'value'));
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useForm } from '@inertiajs/vue3';
|
import { useForm, usePage } from '@inertiajs/vue3';
|
||||||
import { Minus, Plus, Save, Search, ShoppingCart, Trash2 } from '@lucide/vue';
|
import { Minus, Plus, Save, Search, ShoppingCart, Trash2 } from '@lucide/vue';
|
||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
import { toast } from 'vue-sonner';
|
import { toast } from 'vue-sonner';
|
||||||
|
import type { Auth } from '@/types/auth';
|
||||||
import PosCatalogCard from '@/components/admin/manage/PosCatalogCard.vue';
|
import PosCatalogCard from '@/components/admin/manage/PosCatalogCard.vue';
|
||||||
import PosCatalogVariantThumb from '@/components/admin/manage/PosCatalogVariantThumb.vue';
|
import PosCatalogVariantThumb from '@/components/admin/manage/PosCatalogVariantThumb.vue';
|
||||||
import { NumberInput } from '@/components/form/number-input';
|
import { NumberInput } from '@/components/form/number-input';
|
||||||
@ -42,11 +43,13 @@ import type { ProductPriceItem, ProductVariantItem } from '@/types/product';
|
|||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
customers: SelectOption[];
|
customers: SelectOption[];
|
||||||
|
marketings: SelectOption[];
|
||||||
catalog: OrderCatalogItem[];
|
catalog: OrderCatalogItem[];
|
||||||
channels: EnumOption[];
|
channels: EnumOption[];
|
||||||
storePriceTypes: EnumOption[];
|
storePriceTypes: EnumOption[];
|
||||||
initialData?: {
|
initialData?: {
|
||||||
customer_id: string;
|
customer_id: string;
|
||||||
|
marketing_id: string;
|
||||||
channel: string;
|
channel: string;
|
||||||
price_type: string;
|
price_type: string;
|
||||||
discount: string;
|
discount: string;
|
||||||
@ -61,11 +64,23 @@ const props = defineProps<{
|
|||||||
|
|
||||||
const isCreateMode = computed(() => props.method === 'post');
|
const isCreateMode = computed(() => props.method === 'post');
|
||||||
|
|
||||||
|
const page = usePage();
|
||||||
|
const authUser = computed(() => (page.props.auth as Auth).user);
|
||||||
|
const isMarketingUser = computed(() =>
|
||||||
|
authUser.value?.roles?.includes('marketing') ?? false,
|
||||||
|
);
|
||||||
|
|
||||||
const search = ref('');
|
const search = ref('');
|
||||||
const cart = ref<OrderCartItem[]>([]);
|
const cart = ref<OrderCartItem[]>([]);
|
||||||
|
|
||||||
|
// Auto-fill marketing_id with logged-in user's id if they are marketing role
|
||||||
|
const defaultMarketingId = computed(() =>
|
||||||
|
isMarketingUser.value && authUser.value ? String(authUser.value.id) : '',
|
||||||
|
);
|
||||||
|
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
customer_id: '',
|
customer_id: '',
|
||||||
|
marketing_id: defaultMarketingId.value || 'none',
|
||||||
channel: 'store',
|
channel: 'store',
|
||||||
price_type: 'ecer',
|
price_type: 'ecer',
|
||||||
discount: '',
|
discount: '',
|
||||||
@ -80,6 +95,7 @@ function populateForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
form.customer_id = props.initialData.customer_id;
|
form.customer_id = props.initialData.customer_id;
|
||||||
|
form.marketing_id = props.initialData.marketing_id || 'none';
|
||||||
form.channel = props.initialData.channel;
|
form.channel = props.initialData.channel;
|
||||||
form.price_type = props.initialData.price_type;
|
form.price_type = props.initialData.price_type;
|
||||||
form.discount = props.initialData.discount;
|
form.discount = props.initialData.discount;
|
||||||
@ -323,6 +339,12 @@ function buildFormData(): FormData {
|
|||||||
formData.append('customer_id', form.customer_id);
|
formData.append('customer_id', form.customer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (form.marketing_id && form.marketing_id !== 'none') {
|
||||||
|
formData.append('marketing_id', form.marketing_id);
|
||||||
|
} else if (isMarketingUser.value && authUser.value) {
|
||||||
|
formData.append('marketing_id', String(authUser.value.id));
|
||||||
|
}
|
||||||
|
|
||||||
formData.append('channel', form.channel);
|
formData.append('channel', form.channel);
|
||||||
formData.append('price_type', form.price_type);
|
formData.append('price_type', form.price_type);
|
||||||
formData.append('discount', parseRupiah(form.discount));
|
formData.append('discount', parseRupiah(form.discount));
|
||||||
@ -502,6 +524,30 @@ function submit() {
|
|||||||
<FieldError :errors="formErrors(form, 'customer_id')" />
|
<FieldError :errors="formErrors(form, 'customer_id')" />
|
||||||
</Field>
|
</Field>
|
||||||
|
|
||||||
|
<Field>
|
||||||
|
<FieldLabel for="marketing">Marketing</FieldLabel>
|
||||||
|
<Select v-model="form.marketing_id" :disabled="isMarketingUser">
|
||||||
|
<SelectTrigger id="marketing" class="w-full">
|
||||||
|
<SelectValue placeholder="Pilih marketing (opsional)" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectItem value="none">
|
||||||
|
— Tidak ada —
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem v-for="marketing in marketings" :key="marketing.value"
|
||||||
|
:value="String(marketing.value)">
|
||||||
|
{{ marketing.label }}
|
||||||
|
</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<p v-if="isMarketingUser" class="text-xs text-muted-foreground">
|
||||||
|
Otomatis tercatat atas nama Anda.
|
||||||
|
</p>
|
||||||
|
<FieldError :errors="formErrors(form, 'marketing_id')" />
|
||||||
|
</Field>
|
||||||
|
|
||||||
<div v-if="cart.length === 0"
|
<div v-if="cart.length === 0"
|
||||||
class="rounded-lg border border-dashed text-center text-sm text-muted-foreground">
|
class="rounded-lg border border-dashed text-center text-sm text-muted-foreground">
|
||||||
<Empty>
|
<Empty>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import type { EnumOption, OrderCartItem, OrderCatalogItem, SelectOption } from '
|
|||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
customers: SelectOption[];
|
customers: SelectOption[];
|
||||||
|
marketings: SelectOption[];
|
||||||
catalog: OrderCatalogItem[];
|
catalog: OrderCatalogItem[];
|
||||||
channels: EnumOption[];
|
channels: EnumOption[];
|
||||||
storePriceTypes: EnumOption[];
|
storePriceTypes: EnumOption[];
|
||||||
@ -36,6 +37,7 @@ defineProps<{
|
|||||||
|
|
||||||
<OrderPosForm
|
<OrderPosForm
|
||||||
:customers="customers"
|
:customers="customers"
|
||||||
|
:marketings="marketings"
|
||||||
:catalog="catalog"
|
:catalog="catalog"
|
||||||
:channels="channels"
|
:channels="channels"
|
||||||
:store-price-types="storePriceTypes"
|
:store-price-types="storePriceTypes"
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import type { EnumOption, OrderCatalogItem, OrderEditItem, SelectOption } from '
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
order: OrderEditItem;
|
order: OrderEditItem;
|
||||||
customers: SelectOption[];
|
customers: SelectOption[];
|
||||||
|
marketings: SelectOption[];
|
||||||
catalog: OrderCatalogItem[];
|
catalog: OrderCatalogItem[];
|
||||||
channels: EnumOption[];
|
channels: EnumOption[];
|
||||||
storePriceTypes: EnumOption[];
|
storePriceTypes: EnumOption[];
|
||||||
@ -17,6 +18,7 @@ const props = defineProps<{
|
|||||||
|
|
||||||
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) : '',
|
||||||
channel: props.order.channel,
|
channel: props.order.channel,
|
||||||
price_type: props.order.price_type,
|
price_type: props.order.price_type,
|
||||||
discount: String(props.order.discount),
|
discount: String(props.order.discount),
|
||||||
@ -56,6 +58,7 @@ const initialData = computed(() => ({
|
|||||||
|
|
||||||
<OrderPosForm
|
<OrderPosForm
|
||||||
:customers="customers"
|
:customers="customers"
|
||||||
|
:marketings="marketings"
|
||||||
:catalog="catalog"
|
:catalog="catalog"
|
||||||
:channels="channels"
|
:channels="channels"
|
||||||
:store-price-types="storePriceTypes"
|
:store-price-types="storePriceTypes"
|
||||||
|
|||||||
@ -1,15 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Head, Link } from '@inertiajs/vue3';
|
|
||||||
import { Plus } from '@lucide/vue';
|
|
||||||
import { computed, ref, watch } from 'vue';
|
|
||||||
import OrderGroupedTable from '@/components/admin/manage/orders/OrderGroupedTable.vue';
|
import OrderGroupedTable from '@/components/admin/manage/orders/OrderGroupedTable.vue';
|
||||||
import ThermalPrinterConnectButton from '@/components/admin/manage/orders/ThermalPrinterConnectButton.vue';
|
import ThermalPrinterConnectButton from '@/components/admin/manage/orders/ThermalPrinterConnectButton.vue';
|
||||||
import { Button } from '@/components/ui/button';
|
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 { PaginatedOrders } from '@/types/order';
|
import type { PaginatedOrders } from '@/types/order';
|
||||||
|
import { Head, Link } from '@inertiajs/vue3';
|
||||||
|
import { Plus } from '@lucide/vue';
|
||||||
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
orders: PaginatedOrders;
|
orders: PaginatedOrders;
|
||||||
@ -37,11 +40,11 @@ const tablePagination = computed(() => ({
|
|||||||
total: props.orders.total,
|
total: props.orders.total,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const firstItem = computed(() => (
|
const firstItem = computed(() =>
|
||||||
props.orders.data.length > 0
|
props.orders.data.length > 0
|
||||||
? (props.orders.current_page - 1) * props.orders.per_page + 1
|
? (props.orders.current_page - 1) * props.orders.per_page + 1
|
||||||
: 0
|
: 0,
|
||||||
));
|
);
|
||||||
|
|
||||||
watch(search, (value) => {
|
watch(search, (value) => {
|
||||||
setSearch(value);
|
setSearch(value);
|
||||||
@ -59,17 +62,20 @@ watch(
|
|||||||
<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">
|
<h2 class="text-2xl font-bold tracking-tight">Pesanan</h2>
|
||||||
Pesanan
|
|
||||||
</h2>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex shrink-0 items-center gap-2 self-start sm:self-center">
|
<div
|
||||||
|
class="flex shrink-0 items-center gap-2 self-start sm:self-center"
|
||||||
|
v-if="can('orders.create')"
|
||||||
|
>
|
||||||
<ThermalPrinterConnectButton />
|
<ThermalPrinterConnectButton />
|
||||||
|
|
||||||
<Button v-if="can('orders.create')" as-child>
|
<Button as-child>
|
||||||
<Link href="/admin/manage/orders/create">
|
<Link href="/admin/manage/orders/create">
|
||||||
<Plus class="size-4" />
|
<Plus class="size-4" />
|
||||||
Tambah
|
Tambah
|
||||||
|
|||||||
@ -76,6 +76,7 @@ export type OrderEditItem = {
|
|||||||
id: number;
|
id: number;
|
||||||
order_number: string;
|
order_number: string;
|
||||||
customer_id: number | null;
|
customer_id: number | null;
|
||||||
|
marketing_id: number | null;
|
||||||
channel: string;
|
channel: string;
|
||||||
price_type: string;
|
price_type: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user