fix(FE) : validate number norm & add action, feat user & hak akses
This commit is contained in:
@@ -1,4 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
readonly: false
|
||||
});
|
||||
|
||||
const formData = defineModel<{
|
||||
noRekamMedis: string;
|
||||
noKtp: string;
|
||||
@@ -103,6 +111,16 @@ const savePhoneNumber = () => {
|
||||
const deletePhoneNumber = (index: number) => {
|
||||
formData.value.nomorTelepon.splice(index, 1);
|
||||
};
|
||||
|
||||
// Handle noRekamMedis input - only allow numbers
|
||||
const handleRmInput = (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
const value = input.value;
|
||||
|
||||
// Only allow numbers
|
||||
const numericValue = value.replace(/\D/g, '');
|
||||
formData.value.noRekamMedis = numericValue;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -122,9 +140,13 @@ const deletePhoneNumber = (index: number) => {
|
||||
placeholder="Masukan Nomor RM / Nama Pasien"
|
||||
variant="outlined"
|
||||
maxlength="8"
|
||||
density="comfortable"
|
||||
density="comfortable"
|
||||
type="tel"
|
||||
:rules="[rules.required]"
|
||||
hide-details="auto"
|
||||
:readonly="readonly"
|
||||
:bg-color="readonly ? 'grey-lighten-3' : undefined"
|
||||
@input="handleRmInput"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
|
||||
@@ -145,7 +167,7 @@ const deletePhoneNumber = (index: number) => {
|
||||
<!-- Jenis Kelamin -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-label class="mb-2 font-weight-medium">Jenis Kelamin</v-label>
|
||||
<v-radio-group v-model="formData.jenisKelamin" inline hide-details="auto">
|
||||
<v-radio-group v-model="formData.jenisKelamin" inline hide-details="auto" :readonly="readonly" :disabled="readonly">
|
||||
<v-radio label="Laki-Laki" value="L" color="primary"></v-radio>
|
||||
<v-radio label="Perempuan" value="P" color="primary"></v-radio>
|
||||
</v-radio-group>
|
||||
@@ -197,13 +219,14 @@ const deletePhoneNumber = (index: number) => {
|
||||
:key="index"
|
||||
class="px-4 mb-2"
|
||||
border
|
||||
@click="editPhoneNumber(index)"
|
||||
style="cursor: pointer;"
|
||||
@click="!readonly && editPhoneNumber(index)"
|
||||
:style="readonly ? 'cursor: default;' : 'cursor: pointer;'"
|
||||
>
|
||||
<template #default>
|
||||
<div class="d-flex align-center justify-space-between w-100">
|
||||
<span>{{ phone }}</span>
|
||||
<v-btn
|
||||
v-if="!readonly"
|
||||
icon
|
||||
size="small"
|
||||
variant="text"
|
||||
@@ -219,6 +242,7 @@ const deletePhoneNumber = (index: number) => {
|
||||
|
||||
<!-- Add Button -->
|
||||
<v-btn
|
||||
v-if="!readonly"
|
||||
color="primary"
|
||||
variant="flat"
|
||||
prepend-icon="mdi-plus"
|
||||
@@ -277,7 +301,7 @@ const deletePhoneNumber = (index: number) => {
|
||||
:disabled="!newPhoneNumber.trim() || phoneError !== true"
|
||||
>
|
||||
<v-icon start>mdi-check</v-icon>
|
||||
Save
|
||||
Simpan
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
@@ -14,6 +14,14 @@ interface TindakanItem {
|
||||
tindakanTambahan?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
readonly: false
|
||||
});
|
||||
|
||||
const diagnosisItems = defineModel<DiagnosisItem[]>('diagnosisItems', { required: true });
|
||||
const tindakanItems = defineModel<TindakanItem[]>('tindakanItems', { required: true });
|
||||
|
||||
@@ -186,8 +194,8 @@ const handleKodeTindakanChange = (kode: string) => {
|
||||
v-for="(item, index) in diagnosisItems"
|
||||
:key="index"
|
||||
class="px-4 mb-2"
|
||||
@click="editDiagnosis(index)"
|
||||
style="cursor: pointer;"
|
||||
@click="!readonly && editDiagnosis(index)"
|
||||
:style="readonly ? 'cursor: default;' : 'cursor: pointer;'"
|
||||
>
|
||||
<template #default>
|
||||
<div class="d-flex align-center justify-space-between w-100">
|
||||
@@ -198,6 +206,7 @@ const handleKodeTindakanChange = (kode: string) => {
|
||||
</div>
|
||||
</div>
|
||||
<v-btn
|
||||
v-if="!readonly"
|
||||
icon
|
||||
size="small"
|
||||
variant="text"
|
||||
@@ -211,7 +220,7 @@ const handleKodeTindakanChange = (kode: string) => {
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
<v-btn color="primary" prepend-icon="mdi-plus" @click="openDiagnosisModal">
|
||||
<v-btn v-if="!readonly" color="primary" prepend-icon="mdi-plus" @click="openDiagnosisModal">
|
||||
Tambah Diagnosa
|
||||
</v-btn>
|
||||
</div>
|
||||
@@ -235,8 +244,8 @@ const handleKodeTindakanChange = (kode: string) => {
|
||||
v-for="(item, index) in tindakanItems"
|
||||
:key="index"
|
||||
class="px-4 mb-2"
|
||||
@click="editTindakan(index)"
|
||||
style="cursor: pointer;"
|
||||
@click="!readonly && editTindakan(index)"
|
||||
:style="readonly ? 'cursor: default;' : 'cursor: pointer;'"
|
||||
>
|
||||
<template #default>
|
||||
<div class="d-flex align-center justify-space-between w-100">
|
||||
@@ -247,6 +256,7 @@ const handleKodeTindakanChange = (kode: string) => {
|
||||
</div>
|
||||
</div>
|
||||
<v-btn
|
||||
v-if="!readonly"
|
||||
icon
|
||||
size="small"
|
||||
variant="text"
|
||||
@@ -260,7 +270,7 @@ const handleKodeTindakanChange = (kode: string) => {
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
<v-btn color="primary" prepend-icon="mdi-plus" @click="openTindakanModal">
|
||||
<v-btn v-if="!readonly" color="primary" prepend-icon="mdi-plus" @click="openTindakanModal">
|
||||
Tambah Tindakan
|
||||
</v-btn>
|
||||
</div>
|
||||
@@ -336,7 +346,7 @@ const handleKodeTindakanChange = (kode: string) => {
|
||||
:disabled="!diagnosisForm.kodeDiagnosa.trim() || !diagnosisForm.diagnosa.trim()"
|
||||
>
|
||||
<v-icon start>mdi-check</v-icon>
|
||||
Save
|
||||
Simpan
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -414,7 +424,7 @@ const handleKodeTindakanChange = (kode: string) => {
|
||||
:disabled="!tindakanForm.kodeTindakan.trim() || !tindakanForm.tindakan.trim()"
|
||||
>
|
||||
<v-icon start>mdi-check</v-icon>
|
||||
Save
|
||||
Simpan
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
@@ -11,6 +11,14 @@ interface Dokter {
|
||||
satuan_kerja: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
readonly: false
|
||||
});
|
||||
|
||||
const rencanaOperasiData = defineModel<{
|
||||
spesialis: number | null | undefined;
|
||||
subSpesialis: number | null | undefined;
|
||||
@@ -146,6 +154,9 @@ const toggleDokterSelection = (dokterId: number) => {
|
||||
density="comfortable"
|
||||
:rules="[rules.required]"
|
||||
hide-details="auto"
|
||||
:readonly="readonly"
|
||||
:disabled="readonly"
|
||||
:bg-color="readonly ? 'grey-lighten-3' : undefined"
|
||||
></v-autocomplete>
|
||||
</v-col>
|
||||
|
||||
@@ -160,7 +171,9 @@ const toggleDokterSelection = (dokterId: number) => {
|
||||
density="comfortable"
|
||||
:rules="[rules.required]"
|
||||
hide-details="auto"
|
||||
:disabled="!rencanaOperasiData.spesialis"
|
||||
:disabled="readonly || !rencanaOperasiData.spesialis"
|
||||
:readonly="readonly"
|
||||
:bg-color="readonly ? 'grey-lighten-3' : undefined"
|
||||
></v-autocomplete>
|
||||
</v-col>
|
||||
|
||||
@@ -182,8 +195,9 @@ const toggleDokterSelection = (dokterId: number) => {
|
||||
<v-list-item
|
||||
v-for="(item, index) in dokterPelaksanaItems"
|
||||
:key="index"
|
||||
@click="openDokterModal"
|
||||
@click="!readonly && openDokterModal()"
|
||||
class="px-4 mb-2"
|
||||
:style="readonly ? 'cursor: default;' : 'cursor: pointer;'"
|
||||
>
|
||||
<template #default>
|
||||
<div class="d-flex align-center justify-space-between w-100">
|
||||
@@ -194,6 +208,7 @@ const toggleDokterSelection = (dokterId: number) => {
|
||||
</div>
|
||||
</div>
|
||||
<v-btn
|
||||
v-if="!readonly"
|
||||
icon
|
||||
size="small"
|
||||
variant="text"
|
||||
@@ -207,7 +222,7 @@ const toggleDokterSelection = (dokterId: number) => {
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
<v-btn color="primary" prepend-icon="mdi-plus" @click="openDokterModal">
|
||||
<v-btn v-if="!readonly" color="primary" prepend-icon="mdi-plus" @click="openDokterModal">
|
||||
Tambah Dokter Pelaksana
|
||||
</v-btn>
|
||||
</v-col>
|
||||
@@ -222,6 +237,8 @@ const toggleDokterSelection = (dokterId: number) => {
|
||||
density="comfortable"
|
||||
:rules="[rules.required]"
|
||||
hide-details="auto"
|
||||
:readonly="readonly"
|
||||
:bg-color="readonly ? 'grey-lighten-3' : undefined"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
|
||||
@@ -236,6 +253,9 @@ const toggleDokterSelection = (dokterId: number) => {
|
||||
density="comfortable"
|
||||
:rules="[rules.required]"
|
||||
hide-details="auto"
|
||||
:readonly="readonly"
|
||||
:disabled="readonly"
|
||||
:bg-color="readonly ? 'grey-lighten-3' : undefined"
|
||||
></v-autocomplete>
|
||||
</v-col>
|
||||
|
||||
@@ -247,6 +267,8 @@ const toggleDokterSelection = (dokterId: number) => {
|
||||
variant="outlined"
|
||||
rows="5"
|
||||
hide-details="auto"
|
||||
:readonly="readonly"
|
||||
:bg-color="readonly ? 'grey-lighten-3' : undefined"
|
||||
></v-textarea>
|
||||
</v-col>
|
||||
|
||||
@@ -258,6 +280,8 @@ const toggleDokterSelection = (dokterId: number) => {
|
||||
variant="outlined"
|
||||
rows="3"
|
||||
hide-details="auto"
|
||||
:readonly="readonly"
|
||||
:bg-color="readonly ? 'grey-lighten-3' : undefined"
|
||||
></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -357,7 +381,7 @@ const toggleDokterSelection = (dokterId: number) => {
|
||||
:disabled="selectedDokters.length === 0"
|
||||
>
|
||||
<v-icon start>mdi-check</v-icon>
|
||||
Save ({{ selectedDokters.length }})
|
||||
Simpan ({{ selectedDokters.length }})
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
readonly: false
|
||||
});
|
||||
|
||||
const statusPasienData = defineModel<{
|
||||
tanggalSelesai: string;
|
||||
statusOperasi: string;
|
||||
@@ -24,7 +32,8 @@ const statusPasienData = defineModel<{
|
||||
size="large"
|
||||
:variant="statusPasienData.statusOperasi === 'Belum' ? 'flat' : 'outlined'"
|
||||
:color="statusPasienData.statusOperasi === 'Belum' ? 'primary' : 'default'"
|
||||
@click="statusPasienData.statusOperasi = 'Belum'"
|
||||
@click="!readonly && (statusPasienData.statusOperasi = 'Belum')"
|
||||
:disabled="readonly"
|
||||
>
|
||||
<v-icon left class="mr-2">{{ statusPasienData.statusOperasi === 'Belum' ? 'mdi-radiobox-marked' : 'mdi-radiobox-blank' }}</v-icon>
|
||||
Belum
|
||||
@@ -36,7 +45,8 @@ const statusPasienData = defineModel<{
|
||||
size="large"
|
||||
:variant="statusPasienData.statusOperasi === 'Selesai' ? 'flat' : 'outlined'"
|
||||
:color="statusPasienData.statusOperasi === 'Selesai' ? 'primary' : 'default'"
|
||||
@click="statusPasienData.statusOperasi = 'Selesai'"
|
||||
@click="!readonly && (statusPasienData.statusOperasi = 'Selesai')"
|
||||
:disabled="readonly"
|
||||
>
|
||||
<v-icon left class="mr-2">{{ statusPasienData.statusOperasi === 'Selesai' ? 'mdi-radiobox-marked' : 'mdi-radiobox-blank' }}</v-icon>
|
||||
Selesai
|
||||
@@ -48,7 +58,8 @@ const statusPasienData = defineModel<{
|
||||
size="large"
|
||||
:variant="statusPasienData.statusOperasi === 'Tunda' ? 'flat' : 'outlined'"
|
||||
:color="statusPasienData.statusOperasi === 'Tunda' ? 'primary' : 'default'"
|
||||
@click="statusPasienData.statusOperasi = 'Tunda'"
|
||||
@click="!readonly && (statusPasienData.statusOperasi = 'Tunda')"
|
||||
:disabled="readonly"
|
||||
>
|
||||
<v-icon left class="mr-2">{{ statusPasienData.statusOperasi === 'Tunda' ? 'mdi-radiobox-marked' : 'mdi-radiobox-blank' }}</v-icon>
|
||||
Tunda
|
||||
@@ -60,7 +71,8 @@ const statusPasienData = defineModel<{
|
||||
size="large"
|
||||
:variant="statusPasienData.statusOperasi === 'Batal' ? 'flat' : 'outlined'"
|
||||
:color="statusPasienData.statusOperasi === 'Batal' ? 'primary' : 'default'"
|
||||
@click="statusPasienData.statusOperasi = 'Batal'"
|
||||
@click="!readonly && (statusPasienData.statusOperasi = 'Batal')"
|
||||
:disabled="readonly"
|
||||
>
|
||||
<v-icon left class="mr-2">{{ statusPasienData.statusOperasi === 'Batal' ? 'mdi-radiobox-marked' : 'mdi-radiobox-blank' }}</v-icon>
|
||||
Batal
|
||||
@@ -78,6 +90,8 @@ const statusPasienData = defineModel<{
|
||||
variant="outlined"
|
||||
density="comfortable"
|
||||
hide-details="auto"
|
||||
:readonly="readonly"
|
||||
:bg-color="readonly ? 'grey-lighten-3' : undefined"
|
||||
></v-text-field>
|
||||
</v-col>
|
||||
|
||||
@@ -87,9 +101,11 @@ const statusPasienData = defineModel<{
|
||||
<v-textarea
|
||||
v-model="statusPasienData.keteranganStatus"
|
||||
variant="outlined"
|
||||
rows="5"
|
||||
placeholder="Keterangan status tunda / batal"
|
||||
rows="3"
|
||||
hide-details="auto"
|
||||
:readonly="readonly"
|
||||
:bg-color="readonly ? 'grey-lighten-3' : undefined"
|
||||
placeholder="Keterangan status tunda / batal"
|
||||
></v-textarea>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
@@ -7,20 +7,37 @@ interface Header {
|
||||
align?: 'start' | 'center' | 'end';
|
||||
}
|
||||
|
||||
interface Action {
|
||||
icon: string;
|
||||
color?: string;
|
||||
tooltip?: string;
|
||||
event: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
headers: Header[];
|
||||
items: any[];
|
||||
search?: string;
|
||||
itemsPerPage?: number;
|
||||
minWidth?: string;
|
||||
actions?: Action[];
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
search: '',
|
||||
itemsPerPage: 10,
|
||||
minWidth: '1200px'
|
||||
minWidth: '1200px',
|
||||
actions: () => []
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: string, item: any): void;
|
||||
}>();
|
||||
|
||||
const handleAction = (event: string, item: any) => {
|
||||
emit(event, item);
|
||||
};
|
||||
|
||||
const currentPage = ref(1);
|
||||
const itemsPerPageLocal = ref(props.itemsPerPage);
|
||||
</script>
|
||||
@@ -64,6 +81,28 @@ const itemsPerPageLocal = ref(props.itemsPerPage);
|
||||
</v-chip>
|
||||
</slot>
|
||||
</template>
|
||||
|
||||
<!-- Action slot if enabled -->
|
||||
<template v-if="actions && actions.length > 0" #item.actions="{ item }">
|
||||
<slot name="item.actions" :item="item">
|
||||
<div class="d-flex ga-2">
|
||||
<v-btn
|
||||
v-for="(action, index) in actions"
|
||||
:key="index"
|
||||
icon
|
||||
size="small"
|
||||
variant="text"
|
||||
:color="action.color || 'primary'"
|
||||
@click="handleAction(action.event, item)"
|
||||
>
|
||||
<v-icon>{{ action.icon }}</v-icon>
|
||||
<v-tooltip v-if="action.tooltip" activator="parent" location="top">
|
||||
{{ action.tooltip }}
|
||||
</v-tooltip>
|
||||
</v-btn>
|
||||
</div>
|
||||
</slot>
|
||||
</template>
|
||||
</v-data-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user