DONE: layout letter with letterhead and footer, notification for newly...
This commit is contained in:
@@ -1,10 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { datePickerMonthSelect } from "@mykopkb/core/styles/datepicker.styles";
|
||||
import {
|
||||
NativeSelect,
|
||||
NativeSelectOption,
|
||||
} from "@/components/ui/native-select";
|
||||
import type { Api } from "@zag-js/date-picker";
|
||||
import { inject } from "vue";
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import {
|
||||
NativeSelect,
|
||||
NativeSelectOption,
|
||||
} from "@/components/ui/native-select";
|
||||
|
||||
import { datePickerYearSelect } from "@mykopkb/core/styles/datepicker.styles";
|
||||
import type { Api } from "@zag-js/date-picker";
|
||||
import { inject } from "vue";
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { input } from "@mykopkb/core/styles/input.styles";
|
||||
import { nativeSelect } from "@mykopkb/core/styles/native-select.styles";
|
||||
|
||||
const { class: className, ...props } = defineProps<{
|
||||
class?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<select :class="cn(input, nativeSelect, className)" v-bind="{ ...props, ...$attrs }">
|
||||
<slot />
|
||||
</select>
|
||||
</template>
|
||||
@@ -1,14 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { nativeSelectOption } from "@mykopkb/core/styles/native-select.styles";
|
||||
|
||||
const { class: className, ...props } = defineProps<{
|
||||
class?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<option :class="cn(nativeSelectOption, className)" v-bind="{ ...props, ...$attrs }">
|
||||
<slot />
|
||||
</option>
|
||||
</template>
|
||||
@@ -1,14 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { NativeSelectOptGroup } from "@mykopkb/core/styles/native-select.styles";
|
||||
|
||||
const { class: className, ...props } = defineProps<{
|
||||
class?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<optgroup :class="cn(NativeSelectOptGroup, className)" v-bind="{ ...props, ...$attrs }">
|
||||
<slot />
|
||||
</optgroup>
|
||||
</template>
|
||||
@@ -1,3 +0,0 @@
|
||||
export { default as NativeSelect } from "./NativeSelect.vue";
|
||||
export { default as NativeSelectOption } from "./NativeSelectOption.vue";
|
||||
export { default as NativeSelectOptionGroup } from "./NativeSelectOptionGroup.vue";
|
||||
@@ -2,11 +2,13 @@
|
||||
import { cn } from "@mykopkb/core/utils/cn";
|
||||
import { textarea } from "@mykopkb/core/styles/textarea.styles";
|
||||
|
||||
const { class: className, ...props } = defineProps<{
|
||||
const modelValue = defineModel<string>();
|
||||
|
||||
const { class: className } = defineProps<{
|
||||
class?: string;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<textarea :class="cn(textarea, className)" v-bind="{ ...props }" />
|
||||
<textarea v-model="modelValue" :class="cn(textarea, className)" v-bind="$attrs" />
|
||||
</template>
|
||||
|
||||
@@ -43,10 +43,17 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from '@/components/ui/table';
|
||||
import * as select from '@zag-js/select';
|
||||
import {
|
||||
NativeSelect,
|
||||
NativeSelectOption,
|
||||
} from '@/components/ui/native-select';
|
||||
SelectRoot,
|
||||
SelectControl,
|
||||
SelectTrigger,
|
||||
SelectValueText,
|
||||
SelectContent,
|
||||
SelectItemGroup,
|
||||
SelectItem,
|
||||
SelectItemText,
|
||||
} from '@/components/ui/select';
|
||||
|
||||
// Type definitions
|
||||
type ColumnType = 'index' | 'select' | 'action' | 'status' | 'id' | 'date' | 'email' | 'phone' | 'priority' | 'category' | 'registration' | 'model' | 'unit' | 'text';
|
||||
@@ -129,6 +136,22 @@ const resizingColumn = ref<string | null>(null);
|
||||
const startX = ref(0);
|
||||
const perPageOptions = [10, 25, 50, 100, 250, 500, 1000];
|
||||
|
||||
type PerPageSelectOption = { label: string; value: number };
|
||||
|
||||
const perPageSelectOptions: PerPageSelectOption[] = perPageOptions.map((option) => ({
|
||||
label: String(option),
|
||||
value: option,
|
||||
}));
|
||||
|
||||
const perPageCollection = select.collection({
|
||||
items: perPageSelectOptions,
|
||||
itemToValue: (item) => item.label,
|
||||
});
|
||||
|
||||
const perPageInitial = computed(() => [
|
||||
String(itemsPerPageModel.value ?? props.pagination?.per_page ?? props.itemsPerPage ?? 10),
|
||||
]);
|
||||
|
||||
const startWidth = ref(0);
|
||||
|
||||
// Helper function to detect column type from key and title
|
||||
@@ -136,9 +159,10 @@ const detectColumnType = (header: TableHeader): ColumnType => {
|
||||
const key = header.key.toLowerCase();
|
||||
const title = header.title.toLowerCase();
|
||||
|
||||
// Index/Number columns
|
||||
if (key === '#' || key === 'index' || key === 'bil' || key === 'no' ||
|
||||
title.includes('bil') || title.includes('no') || title.includes('#')) {
|
||||
// Index/Number columns (Bil./# row counters — not field labels like "No. Permohonan")
|
||||
if (key === '#' || key === 'index' || key === 'bil' ||
|
||||
title.includes('bil') || title === '#' ||
|
||||
/^no\.?\s*$/i.test(title.trim())) {
|
||||
return 'index';
|
||||
}
|
||||
|
||||
@@ -416,8 +440,9 @@ const headerAlignClass = (align?: TableHeader['align']) => {
|
||||
return 'text-left';
|
||||
};
|
||||
|
||||
const handleItemsPerPageChange = (event: Event) => {
|
||||
const value = Number((event.target as HTMLSelectElement).value);
|
||||
const setItemsPerPageValue = (details: { value: string[] }) => {
|
||||
const value = Number(details.value[0]);
|
||||
if (Number.isNaN(value)) return;
|
||||
itemsPerPageModel.value = value;
|
||||
emit('update:items-per-page', value);
|
||||
};
|
||||
@@ -791,12 +816,30 @@ watch(sortBy, (newSort) => {
|
||||
class="flex flex-wrap items-center justify-between gap-4 border-t border-foreground/10 bg-foreground/2 px-4 py-3">
|
||||
<div class="flex items-center gap-2 text-sm text-muted-foreground">
|
||||
<span>Bilangan rekod per halaman:</span>
|
||||
<NativeSelect class="h-9 w-auto min-w-20 px-2 py-1 text-sm"
|
||||
:value="String(itemsPerPageModel ?? pagination.per_page)" @change="handleItemsPerPageChange">
|
||||
<NativeSelectOption v-for="option in perPageOptions" :key="option" :value="String(option)">
|
||||
{{ option }}
|
||||
</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
<SelectRoot
|
||||
:key="perPageInitial[0]"
|
||||
class="w-auto min-w-20"
|
||||
:collection="perPageCollection"
|
||||
:default-value="perPageInitial"
|
||||
@value-change="setItemsPerPageValue"
|
||||
>
|
||||
<SelectControl>
|
||||
<SelectTrigger class="h-9 w-auto min-w-20">
|
||||
<SelectValueText />
|
||||
</SelectTrigger>
|
||||
</SelectControl>
|
||||
<SelectContent>
|
||||
<SelectItemGroup>
|
||||
<SelectItem
|
||||
v-for="item in perPageCollection.items"
|
||||
:key="item.label"
|
||||
:item="item"
|
||||
>
|
||||
<SelectItemText>{{ item.label }}</SelectItemText>
|
||||
</SelectItem>
|
||||
</SelectItemGroup>
|
||||
</SelectContent>
|
||||
</SelectRoot>
|
||||
</div>
|
||||
|
||||
<div class="text-sm text-muted-foreground">
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
NativeSelect,
|
||||
NativeSelectOption,
|
||||
NativeSelectOptionGroup,
|
||||
} from "@/components/ui/native-select";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-20">
|
||||
<div class="grid sm:grid-cols-2">
|
||||
<div
|
||||
class="justify-center items-center flex gap-2 border-b border-e border-foreground/10 p-5 flex-wrap"
|
||||
>
|
||||
<NativeSelect class="w-56">
|
||||
<NativeSelectOption value="">Select status</NativeSelectOption>
|
||||
<NativeSelectOption value="todo">Todo</NativeSelectOption>
|
||||
<NativeSelectOption value="in-progress"
|
||||
>In Progress</NativeSelectOption
|
||||
>
|
||||
<NativeSelectOption value="done">Done</NativeSelectOption>
|
||||
<NativeSelectOption value="cancelled">Cancelled</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
</div>
|
||||
<div
|
||||
class="justify-center items-center flex gap-2 border-b border-e border-foreground/10 p-5 flex-wrap"
|
||||
>
|
||||
<NativeSelect class="w-56">
|
||||
<NativeSelectOption value="">Select department</NativeSelectOption>
|
||||
<NativeSelectOptionGroup label="Engineering">
|
||||
<NativeSelectOption value="frontend">Frontend</NativeSelectOption>
|
||||
<NativeSelectOption value="backend">Backend</NativeSelectOption>
|
||||
<NativeSelectOption value="devops">DevOps</NativeSelectOption>
|
||||
</NativeSelectOptionGroup>
|
||||
<NativeSelectOptionGroup label="Sales">
|
||||
<NativeSelectOption value="sales-rep">Sales Rep</NativeSelectOption>
|
||||
<NativeSelectOption value="account-manager">
|
||||
Account Manager
|
||||
</NativeSelectOption>
|
||||
<NativeSelectOption value="sales-director">
|
||||
Sales Director
|
||||
</NativeSelectOption>
|
||||
</NativeSelectOptionGroup>
|
||||
<NativeSelectOptionGroup label="Operations">
|
||||
<NativeSelectOption value="support">
|
||||
Customer Support
|
||||
</NativeSelectOption>
|
||||
<NativeSelectOption value="product-manager">
|
||||
Product Manager
|
||||
</NativeSelectOption>
|
||||
<NativeSelectOption value="ops-manager">
|
||||
Operations Manager
|
||||
</NativeSelectOption>
|
||||
</NativeSelectOptionGroup>
|
||||
</NativeSelect>
|
||||
</div>
|
||||
<div
|
||||
class="justify-center items-center flex gap-2 border-b border-e border-foreground/10 p-5 flex-wrap"
|
||||
>
|
||||
<NativeSelect class="w-56" disabled>
|
||||
<NativeSelectOption value="">Select status</NativeSelectOption>
|
||||
<NativeSelectOption value="todo">Todo</NativeSelectOption>
|
||||
<NativeSelectOption value="in-progress"
|
||||
>In Progress</NativeSelectOption
|
||||
>
|
||||
<NativeSelectOption value="done">Done</NativeSelectOption>
|
||||
<NativeSelectOption value="cancelled">Cancelled</NativeSelectOption>
|
||||
</NativeSelect>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user