updated page layouts and format
This commit is contained in:
1405
package-lock.json
generated
1405
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,7 @@
|
|||||||
"tailwindcss": "^4.1.11",
|
"tailwindcss": "^4.1.11",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"vite": "^7.0.6",
|
"vite": "^7.0.6",
|
||||||
|
"vite-plugin-vue-devtools": "^8.0.0",
|
||||||
"vue-tsc": "^3.0.4"
|
"vue-tsc": "^3.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
src/assets/guns.gif
Normal file
BIN
src/assets/guns.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.0 MiB |
@@ -53,7 +53,7 @@
|
|||||||
<div class="w-full mt-5">
|
<div class="w-full mt-5">
|
||||||
<div class="flex gap-2" style="flex-direction: column">
|
<div class="flex gap-2" style="flex-direction: column">
|
||||||
<label for="date_purchased">Date Purchased</label>
|
<label for="date_purchased">Date Purchased</label>
|
||||||
<Calendar date-format="mm/dd/yy" v-model="gun.date_purchased"/>
|
<DatePicker date-format="mm/dd/yy" v-model="gun.date_purchased"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full mt-5">
|
<div class="w-full mt-5">
|
||||||
@@ -98,7 +98,7 @@ const router = useRouter()
|
|||||||
const ConfirmPopup = defineAsyncComponent(() => import('primevue/confirmpopup'))
|
const ConfirmPopup = defineAsyncComponent(() => import('primevue/confirmpopup'))
|
||||||
const InputText = defineAsyncComponent(() => import('primevue/inputtext'))
|
const InputText = defineAsyncComponent(() => import('primevue/inputtext'))
|
||||||
const InputNumber = defineAsyncComponent(() => import('primevue/inputnumber'))
|
const InputNumber = defineAsyncComponent(() => import('primevue/inputnumber'))
|
||||||
const Calendar = defineAsyncComponent(() => import('primevue/calendar'))
|
const DatePicker = defineAsyncComponent(() => import('primevue/datepicker'))
|
||||||
const Textarea = defineAsyncComponent(() => import('primevue/textarea'))
|
const Textarea = defineAsyncComponent(() => import('primevue/textarea'))
|
||||||
const FileUpload = defineAsyncComponent(() => import('primevue/fileupload'))
|
const FileUpload = defineAsyncComponent(() => import('primevue/fileupload'))
|
||||||
const Button = defineAsyncComponent(() => import('primevue/button'))
|
const Button = defineAsyncComponent(() => import('primevue/button'))
|
||||||
|
21
src/localstorage/index.ts
Normal file
21
src/localstorage/index.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
export function SaveToLocalStorage<T>(key: string, value: T): void {
|
||||||
|
try {
|
||||||
|
const serializedValue = JSON.stringify(value);
|
||||||
|
localStorage.setItem(key, serializedValue);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error saving to localStorage", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetFromLocalStorage<T>(key: string): T | null {
|
||||||
|
try {
|
||||||
|
const serializedValue = localStorage.getItem(key);
|
||||||
|
if (serializedValue === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return JSON.parse(serializedValue) as T;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error reading from localStorage", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,9 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full flex flex-row flex-wrap justify-around">
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
||||||
<div class="w-full lg:w-1/2 m-5">
|
<div class="w-full">
|
||||||
<GunForm :loading="loading" @upload="getGun" v-if="gun" @save="save" :gun="gun"/>
|
<GunForm :loading="loading" @upload="getGun" v-if="gun" @save="save" :gun="gun"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-10 w-full lg:w-1/2 m-5">
|
|
||||||
|
<div class="">
|
||||||
<Card>
|
<Card>
|
||||||
<template #content v-if="gun != null">
|
<template #content v-if="gun != null">
|
||||||
<h3 class="text-lg font-bold">
|
<h3 class="text-lg font-bold">
|
||||||
@@ -12,12 +13,16 @@
|
|||||||
<div
|
<div
|
||||||
class="p-5 mt-4 flex justify-evenly" v-for="photo in gun.photos">
|
class="p-5 mt-4 flex justify-evenly" v-for="photo in gun.photos">
|
||||||
<div>
|
<div>
|
||||||
<Image preview zoomInDisabled zoomOutDisabled>
|
<Image
|
||||||
<template #image>
|
preview
|
||||||
<img alt="image" :src="`${remoteUrl}/gun/photo/${photo.id}/200/${photo.file_name}`" />
|
:src="`${remoteUrl}/gun/photo/${photo.id}/200/${photo.file_name}`"
|
||||||
</template>
|
>
|
||||||
<template #preview>
|
<template #original="slotProps">
|
||||||
<img :src="`${remoteUrl}/gun/photo/${photo.id}/${photo.file_name}`" alt="image"/>
|
<img
|
||||||
|
:style="slotProps.style"
|
||||||
|
:src="`${remoteUrl}/gun/photo/${photo.id}/${photo.file_name}`"
|
||||||
|
alt="Gun Photo"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</Image>
|
</Image>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,6 +45,7 @@ import {Response} from "../types/Response"
|
|||||||
import {Guns} from "../types/guns";
|
import {Guns} from "../types/guns";
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { useToast } from 'primevue'
|
import { useToast } from 'primevue'
|
||||||
|
import { useConfirm } from 'primevue/useconfirm'
|
||||||
|
|
||||||
const GunForm = defineAsyncComponent(() => import('../components/GunForm.vue'))
|
const GunForm = defineAsyncComponent(() => import('../components/GunForm.vue'))
|
||||||
const ConfirmPopup = defineAsyncComponent(() => import('primevue/confirmpopup'));
|
const ConfirmPopup = defineAsyncComponent(() => import('primevue/confirmpopup'));
|
||||||
@@ -49,6 +55,7 @@ const Card = defineAsyncComponent(() => import('primevue/card'));
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
|
const confirm = useConfirm()
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const gun = ref<Guns | null>(null)
|
const gun = ref<Guns | null>(null)
|
||||||
@@ -65,16 +72,29 @@ function getGun() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deletePhoto(photoId: number) {
|
function deletePhoto(photoId: number) {
|
||||||
|
confirm.require({
|
||||||
|
message: 'Are you sure you want to delete this photo?',
|
||||||
|
acceptClass: 'p-button-danger',
|
||||||
|
acceptIcon: 'fa-solid fa-trash',
|
||||||
|
rejectIcon: 'fa-solid fa-ban',
|
||||||
|
accept: () => {
|
||||||
|
deletePhotoConfirmed(photoId)
|
||||||
|
},
|
||||||
|
reject: () => {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function deletePhotoConfirmed(photoId: number) {
|
||||||
axios.delete(import.meta.env.VITE_API + `/gun/photo/${photoId}`)
|
axios.delete(import.meta.env.VITE_API + `/gun/photo/${photoId}`)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.add({
|
toast.add({
|
||||||
severity: 'success',
|
severity: 'success',
|
||||||
summary: 'Success',
|
summary: 'Success',
|
||||||
detail: 'Photo deleted successfully',
|
detail: 'Photo deleted successfully',
|
||||||
life: 3000
|
life: 3000
|
||||||
})
|
})
|
||||||
getGun()
|
getGun()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function save() {
|
function save() {
|
||||||
|
@@ -1,22 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<Card>
|
<Card class="w-full">
|
||||||
<template #title>
|
<template #title>
|
||||||
Gun List
|
Gun List
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<span class="p-input-icon-left w-full">
|
<span class="p-input-icon-left w-full">
|
||||||
<i class="fa-regular fa-magnifying-glass"></i>
|
<InputGroup class="mb-2">
|
||||||
<InputText v-model="filters.global.value" class="w-full mb-3" placeholder="Search"/>
|
<InputGroupAddon>
|
||||||
|
<i class="fa-solid fa-2x fa-magnifying-glass"></i>
|
||||||
|
</InputGroupAddon>
|
||||||
|
<InputText
|
||||||
|
size="small"
|
||||||
|
v-model="filters.global.value"
|
||||||
|
class="w-full mb-3"
|
||||||
|
placeholder="Search"
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
</span>
|
</span>
|
||||||
<DataTable
|
<DataTable
|
||||||
sortField="make" :sortOrder="1"
|
v-if="sortField"
|
||||||
|
:sortField="sortField.field"
|
||||||
|
:sortOrder="sortField.direction"
|
||||||
:globalFilterFields="['make', 'model']"
|
:globalFilterFields="['make', 'model']"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
filterDisplay="row" v-model:filters="filters" class="p-datatable-sm" :value="guns">
|
filterDisplay="row"
|
||||||
<Column sortable field="id" header="ID"></Column>
|
v-model:filters="filters"
|
||||||
<Column sortable field="make" header="Make"></Column>
|
:value="guns"
|
||||||
<Column sortable field="model" header="Model"></Column>
|
size="small"
|
||||||
<Column sortable field="value_amount" header="Value">
|
@sort="onSortChange"
|
||||||
|
>
|
||||||
|
<Column :sortable="true" field="id" header="ID"></Column>
|
||||||
|
<Column :sortable="true" field="make" header="Make"></Column>
|
||||||
|
<Column :sortable="true" field="model" header="Model"></Column>
|
||||||
|
<Column :sortable="true" field="value_amount" header="Value">
|
||||||
<template #body="slotProps">
|
<template #body="slotProps">
|
||||||
${{ slotProps.data.value_amount }}
|
${{ slotProps.data.value_amount }}
|
||||||
</template>
|
</template>
|
||||||
@@ -38,19 +55,24 @@
|
|||||||
</template>
|
</template>
|
||||||
</Card>
|
</Card>
|
||||||
</template>
|
</template>
|
||||||
|
<script lang="ts">
|
||||||
|
const LocalStorageKey = 'sortField'
|
||||||
|
</script>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, defineAsyncComponent, onMounted, ref } from 'vue'
|
import { computed, defineAsyncComponent, onMounted, ref } from 'vue'
|
||||||
import {Guns} from "../types/guns";
|
import {Guns} from "../types/guns";
|
||||||
import {Response} from "../types/Response";
|
import {Response} from "../types/Response";
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {DataTableFilterMetaData} from "primevue/datatable";
|
import { DataTableFilterMetaData, DataTableSortEvent } from 'primevue/datatable'
|
||||||
import Column from 'primevue/column';
|
import Column from 'primevue/column';
|
||||||
|
import { GetFromLocalStorage, SaveToLocalStorage } from '../localstorage'
|
||||||
|
|
||||||
const Button = defineAsyncComponent(() => import('primevue/button'));
|
const Button = defineAsyncComponent(() => import('primevue/button'));
|
||||||
const Card = defineAsyncComponent(() => import('primevue/card'));
|
const Card = defineAsyncComponent(() => import('primevue/card'));
|
||||||
const InputText = defineAsyncComponent(() => import('primevue/inputtext'));
|
const InputText = defineAsyncComponent(() => import('primevue/inputtext'));
|
||||||
const DataTable = defineAsyncComponent(() => import('primevue/datatable'));
|
const DataTable = defineAsyncComponent(() => import('primevue/datatable'));
|
||||||
|
const InputGroup = defineAsyncComponent(() => import('primevue/inputgroup'));
|
||||||
|
const InputGroupAddon = defineAsyncComponent(() => import('primevue/inputgroupaddon'));
|
||||||
|
|
||||||
const guns = ref<Guns[]>([])
|
const guns = ref<Guns[]>([])
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
@@ -61,6 +83,12 @@ const filters = ref<{global: DataTableFilterMetaData}>({
|
|||||||
} as DataTableFilterMetaData
|
} as DataTableFilterMetaData
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const sortField = ref<{ field: string, direction: number } | null>(GetFromLocalStorage(LocalStorageKey))
|
||||||
|
|
||||||
|
if (!sortField.value) {
|
||||||
|
sortField.value = { field: 'id', direction: 1 }
|
||||||
|
}
|
||||||
|
|
||||||
const total = computed(() => {
|
const total = computed(() => {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
|
|
||||||
@@ -82,6 +110,26 @@ function fetchGuns() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onSortChange(event: DataTableSortEvent) {
|
||||||
|
let field = event.sortField || 'id'
|
||||||
|
let direction = event.sortOrder || 1
|
||||||
|
|
||||||
|
if (typeof field !== 'string') {
|
||||||
|
field = 'id'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!direction || typeof direction === 'undefined') {
|
||||||
|
direction = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveToLocalStorage<{ field: string, direction: number }>(LocalStorageKey, {
|
||||||
|
field: field,
|
||||||
|
direction: direction
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('onSortChange', event)
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchGuns()
|
fetchGuns()
|
||||||
})
|
})
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<Card class="w-full lg:w-1/2 text-center">
|
<Card class="w-full md:w-1/2 text-center">
|
||||||
<template #title> Welcome to Your Gun Database </template>
|
<template #title> Welcome to Your Gun Database</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
|
<img :src="imgUrl" alt="Gun Database Logo" />
|
||||||
<router-link to="/guns">
|
<router-link to="/guns">
|
||||||
<Button>Go To Gun List</Button>
|
<Button class="mt-3">Go To Gun List</Button>
|
||||||
</router-link>
|
</router-link>
|
||||||
</template>
|
</template>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -11,6 +12,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
|
import imgUrl from '../assets/guns.gif'
|
||||||
|
|
||||||
const Card = defineAsyncComponent(() => import('primevue/card'));
|
const Card = defineAsyncComponent(() => import('primevue/card'));
|
||||||
const Button = defineAsyncComponent(() => import('primevue/button'));
|
const Button = defineAsyncComponent(() => import('primevue/button'));
|
||||||
</script>
|
</script>
|
@@ -1,8 +1,13 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import tailwindcss from '@tailwindcss/vite'
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
|
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [vue(), tailwindcss()],
|
plugins: [vue(), tailwindcss(), vueDevTools(
|
||||||
|
{
|
||||||
|
launchEditor: 'webstorm'
|
||||||
|
}
|
||||||
|
)],
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user