character skin

This commit is contained in:
2025-07-21 14:48:10 +07:00
parent 0bba267091
commit 9293c4cde5
6 changed files with 681 additions and 48 deletions
+35 -30
View File
@@ -2,7 +2,7 @@
<div>
<ul class="flex space-x-2 rtl:space-x-reverse">
<li>
<a href="javascript:;" class="text-primary hover:underline">Konten</a>
<a href="javascript:;" class="text-primary hover:underline">Karakter</a>
</li>
<li class="before:mr-2 before:content-['/'] rtl:before:ml-2">
<NuxtLink to="/character/characters/list" class="text-primary hover:underline">Daftar Karakter</NuxtLink>
@@ -15,7 +15,7 @@
<div class="grid grid-cols-1 gap-6 pt-5">
<div class="panel">
<div class="mb-5 flex items-center justify-between">
<h5 class="text-lg font-semibold dark:text-white-light">Tambah Konten</h5>
<h5 class="text-lg font-semibold dark:text-white-light">Tambah Karakter</h5>
<NuxtLink to="/character/characters/list" class="dark:text-white-light btn btn-dark !py-1">
<icon-arrow-backward class="me-1" />
Daftar
@@ -53,7 +53,7 @@
<label for="grade">Jenis Kelamin</label>
<div class="grid grid-cols-1 gap-2">
<label class="inline-flex">
<input type="radio" v-model="form.sex" class="form-radio" value="1" checked />
<input type="radio" v-model="form.sex" class="form-radio" value="1" />
<span>Laki-laki</span>
</label>
<label class="inline-flex">
@@ -110,8 +110,8 @@
</div>
</div>
<div class="flex items-center ltr:ml-auto mt-8">
<button type="submit" class="btn btn-success !py-1"><icon-save class="me-1" /> Simpan</button>
<button type="reset" class="btn btn-dark !py-1 ml-1"><icon-restore class="me-1" />Reset</button>
<button type="submit" class="btn btn-success !py-1" :disabled="isLoading"><icon-save class="me-1" /> {{ isLoading ? 'Menyimpan...' : 'Simpan' }}</button>
<button type="button" class="btn btn-dark !py-1 ml-1" @click="resetForm"><icon-restore class="me-1" />Reset</button>
</div>
</form>
</div>
@@ -132,7 +132,7 @@
const form = ref({
name: '',
decription: '',
description: '',
sex: null,
type: '',
featured_image: '',
@@ -154,38 +154,23 @@
const config = useRuntimeConfig();
const typeOptions = [{ "value": "manusia", "label": "Manusia" }, { "value": "hewan", "label": "Hewan" } ]
const params = reactive({
current_page: 1,
pagesize: 10,
sort_column: 'id',
sort_direction: 'asc',
});
const isLoading = ref(false);
const { data: characters } = await useAsyncData('characters',
() => $fetch(`${config.public.apiBase}character/characters/`, {
params: {
page: params.current_page,
page_size: 50
}
}), {
watch: [params]
}
);
const featuredImageUploader = ref(null);
const featuredIconUploader = ref(null);
onMounted(async () => {
const fileupload = await import('file-upload-with-preview');
let FileUploadWithPreview = fileupload.default;
// single image upload
new FileUploadWithPreview('featuredImage', {
featuredImageUploader.value = new FileUploadWithPreview('featuredImage', {
images: {
baseImage: '/assets/images/file-preview.svg',
backgroundImage: '',
},
});
// single image upload
new FileUploadWithPreview('featuredIcon', {
featuredIconUploader.value = new FileUploadWithPreview('featuredIcon', {
images: {
baseImage: '/assets/images/file-preview.svg',
backgroundImage: '',
@@ -204,7 +189,7 @@
formData.append('name', form.value.name);
formData.append('sex', form.value.sex);
formData.append('type', form.value.type.value);
formData.append('type', form.value.type?.value ?? '');
formData.append('description', form.value.description);
if (form.value.featured_image) {
@@ -214,17 +199,18 @@
formData.append('featured_icon', form.value.featured_icon);
}
isLoading.value = true;
await $fetch(`${config.public.apiBase}character/characters/`, {
method: 'POST',
body: formData
}).then(() => {
//redirect
router.push({ path: "/character/characters/list" });
})
.catch((error) => {
//assign response error data to state "errors"
console.log(error);
})
.finally(() => {
isLoading.value = false;
});
}
@@ -255,4 +241,23 @@
form.value.featured_icon = file
}
const resetForm = () => {
form.value = {
name: '',
description: '',
sex: null,
type: '',
featured_image: '',
featured_icon: '',
};
isSubmitted.value = false;
$validate.value.form.$reset();
featuredImageUploader?.value?.clearPreviewPanel();
featuredIconUploader?.value?.clearPreviewPanel();
};
</script>
+37 -17
View File
@@ -24,9 +24,9 @@
<div class="mb-5">
<div class="datatable">
<vue3-datatable
:rows="characters?.results"
:rows="rows"
:columns="cols"
:totalRows="characters?.count"
:totalRows="totalRows"
:isServerMode=true
:page="params.current_page"
:pageSize="params.pagesize"
@@ -40,8 +40,8 @@
previousArrow='<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-4.5 h-4.5 rtl:rotate-180"> <path d="M15 5L9 12L15 19" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>'
nextArrow='<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-4.5 h-4.5 rtl:rotate-180"> <path d="M9 5L15 12L9 19" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> </svg>'
>
<template #id="data">
<div class="flex gap-1">
<template #actions="data">
<div class="flex justify-end gap-1">
<button type="button" class="btn btn-success !py-1" @click="viewData(data.value)">
<icon-edit class="me-1" />
Edit
@@ -63,7 +63,8 @@
<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue';
import Vue3Datatable from '@bhplugin/vue3-datatable';
useHead({ title: 'Daftar Konten' });
import { useDebounceFn } from '@vueuse/core';
useHead({ title: 'Daftar Karakter' });
const router = useRouter();
const config = useRuntimeConfig();
@@ -71,9 +72,15 @@
const cols =
ref([
{ field: 'name', title: 'Nama' },
{ field: 'id', title: 'Aksi' },
]) || [];
{
field: 'actions',
title: 'Aksi',
headerClass: 'text-right',
cellClass: 'text-right',
sort: false,
width: '150px'
}
]);
const params = reactive({
search: null,
@@ -83,6 +90,8 @@
sort_direction: 'asc',
});
const rows = computed(() => characters.value?.results ?? []);
const totalRows = computed(() => characters.value?.count ?? 0);
const { data: characters } = await useAsyncData('characters',
() => {
return $fetch(`${config.public.apiBase}/character/characters/`, {
@@ -93,9 +102,20 @@
search: params.search
}})
}, {
watch: [params]
watch: [params],
default: () => ({
results: [],
count: 0,
}),
}
);
const debouncedSearch = useDebounceFn(() => {
params.current_page = 1;
refreshNuxtData('characters');
}, 500);
watch(() => params.search, debouncedSearch);
const changeServer = (data: any) => {
params.pagesize = data.pagesize;
@@ -115,13 +135,13 @@
};
const deleteData = async (data: any) => {
//delete data with API
await $fetch(`${config.public.apiBase}character/characters/${data.id}/`, {
method: 'DELETE'
});
//refersh data posts
refreshNuxtData('characters');
try {
await $fetch(`${config.public.apiBase}/character/characters/${data.id}/`, {
method: 'DELETE',
});
refreshNuxtData('characters');
} catch (err) {
console.error('Gagal menghapus data', err);
}
}
</script>