Initial commit

This commit is contained in:
2025-08-04 16:33:07 +03:30
commit f798e8e35c
9595 changed files with 1208683 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
<template>
<v-dialog
:model-value="modelValue"
@update:model-value="handleClose"
:max-width="isMobile ? '95%' : '500px'"
persistent
>
<v-card>
<v-card-title class="d-flex align-center">
<VIcon icon="tabler-alert-triangle" color="error" class="me-3" />
<span class="text-h6">Confirm Delete</span>
</v-card-title>
<v-divider />
<v-card-text class="py-6">
<div class="text-body-1 mb-4">
Are you sure you want to delete this user?
</div>
<v-card
v-if="selectedRowData"
variant="outlined"
class="pa-3 bg-grey-lighten-5"
>
<div class="d-flex flex-column gap-2">
<div class="d-flex align-center">
<VIcon icon="tabler-user" size="small" class="me-2" />
<strong class="me-2">Name:</strong>
{{ selectedRowData.fullName || 'Unknown' }}
</div>
<div class="d-flex align-center">
<VIcon icon="tabler-mail" size="small" class="me-2" />
<strong class="me-2">Email:</strong>
{{ selectedRowData.email || 'Unknown' }}
</div>
</div>
</v-card>
<v-alert
type="warning"
variant="tonal"
class="mt-4"
>
<template #prepend>
<VIcon icon="tabler-alert-circle" />
</template>
<template #text>
This action cannot be undone and will permanently delete the user data.
</template>
</v-alert>
</v-card-text>
<v-divider />
<v-card-actions class="pa-4">
<v-spacer />
<v-btn
variant="outlined"
color="grey"
@click="handleCancel"
:class="isMobile ? 'flex-grow-1 me-2' : ''"
>
<template #prepend>
<VIcon icon="tabler-x" />
</template>
Cancel
</v-btn>
<v-btn
variant="flat"
color="error"
@click="handleConfirm"
:class="isMobile ? 'flex-grow-1' : ''"
>
<template #prepend>
<VIcon icon="tabler-trash" />
</template>
Delete
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup>
defineProps({
modelValue: {
type: Boolean,
default: false
},
selectedRowData: {
type: Object,
default: null
},
isMobile: {
type: Boolean,
default: false
}
})
const emit = defineEmits(['update:model-value', 'confirm', 'cancel'])
const handleClose = (value) => {
if (!value) {
emit('update:model-value', false)
emit('cancel')
}
}
const handleCancel = () => {
emit('update:model-value', false)
emit('cancel')
}
const handleConfirm = () => {
emit('update:model-value', false)
emit('confirm')
}
</script>
<style scoped>
.v-card-title {
background: rgba(var(--v-theme-error), 0.1);
border-bottom: 1px solid rgba(var(--v-theme-error), 0.2);
}
.v-alert {
border-left: 4px solid rgb(var(--v-theme-warning));
}
@media (max-width: 600px) {
.v-card-actions {
flex-direction: column;
gap: 8px;
}
.v-btn {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,113 @@
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
isMobile: Boolean,
isTablet: Boolean,
quickFilter: String
})
const emit = defineEmits(['update:quick-filter', 'export-csv', 'export-excel', 'export-pdf'])
const updateQuickFilter = value => {
emit('update:quick-filter', value)
}
const handleExportCsv = () => emit('export-csv')
const handleExportExcel = () => emit('export-excel')
const handleExportPdf = () => emit('export-pdf')
</script>
<template>
<v-card-title
class="d-flex align-center py-4 px-4"
:class="{
'flex-column ga-4': isMobile,
'flex-row': !isMobile
}"
>
<div class="d-flex align-center">
<span class="text-h6 font-weight-medium">Data Table</span>
</div>
<v-spacer v-if="!isMobile" />
<div
class="d-flex align-center"
:class="{
'flex-column ga-3 w-100': isMobile,
'ga-3': !isMobile
}"
>
<v-text-field
:model-value="quickFilter"
@update:model-value="updateQuickFilter"
append-inner-icon="tabler-search"
label="Search..."
single-line
hide-details
density="compact"
:style="getSearchFieldWidth"
variant="outlined"
clearable
placeholder="Search in all columns..."
/>
<div
class="d-flex ga-2"
:class="{ 'w-100': isMobile }"
>
<v-btn
color="success"
variant="outlined"
size="small"
@click="handleExportCsv"
:class="{ 'flex-1-1-0': isMobile }"
>
<VIcon icon="tabler-file-text" size="18" />
<span v-if="!isMobile" class="ms-2">Export CSV</span>
<span v-else-if="isTablet" class="ms-1">CSV</span>
<span v-else class="ms-1 text-caption">CSV</span>
</v-btn>
<v-btn
color="primary"
variant="outlined"
size="small"
@click="handleExportExcel"
:class="{ 'flex-1-1-0': isMobile }"
>
<VIcon icon="tabler-file-spreadsheet" size="18" />
<span v-if="!isMobile" class="ms-2">Export Excel</span>
<span v-else-if="isTablet" class="ms-1">Excel</span>
<span v-else class="ms-1 text-caption">Excel</span>
</v-btn>
<v-btn
color="error"
variant="outlined"
size="small"
@click="handleExportPdf"
:class="{ 'flex-1-1-0': isMobile }"
>
<VIcon icon="tabler-file-filled" size="18" />
<span v-if="!isMobile" class="ms-2">Export PDF</span>
<span v-else-if="isTablet" class="ms-1">PDF</span>
<span v-else class="ms-1 text-caption">PDF</span>
</v-btn>
</div>
</div>
</v-card-title>
</template>
<script>
export default {
computed: {
getSearchFieldWidth() {
if (this.isMobile) return 'width: 100%'
if (this.isTablet) return 'width: 400px'
return 'width: 500px'
}
}
}
</script>

View File

@@ -0,0 +1,430 @@
<script setup>
const props = defineProps({
modelValue: Boolean,
selectedRowData: Object,
isMobile: Boolean
})
const emit = defineEmits(['update:modelValue', 'save-user'])
const isEditMode = ref(false)
const editedData = ref({})
watch(() => props.modelValue, (newVal) => {
if (newVal && props.selectedRowData) {
editedData.value = { ...props.selectedRowData }
}
})
const handleEnterKey = () => {
if (isEditMode.value) {
saveChanges()
}
}
const closeDialog = () => {
isEditMode.value = false
emit('update:modelValue', false)
}
const toggleEditMode = () => {
if (!isEditMode.value) {
editedData.value = { ...props.selectedRowData }
}
isEditMode.value = !isEditMode.value
}
const saveChanges = () => {
emit('save-user', editedData.value)
isEditMode.value = false
}
const cancelEdit = () => {
editedData.value = { ...props.selectedRowData }
isEditMode.value = false
}
const formatDate = (dateValue) => {
if (!dateValue) return 'N/A'
const date = new Date(dateValue)
if (isNaN(date.getTime())) return 'Invalid Date'
const day = date.getDate().toString().padStart(2, '0')
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const year = date.getFullYear()
return `${day}/${month}/${year}`
}
const formatDateForInput = (dateValue) => {
if (!dateValue) return ''
const date = new Date(dateValue)
if (isNaN(date.getTime())) return ''
return date.toISOString().split('T')[0]
}
const formatSalary = (salaryValue) => {
if (!salaryValue) return 'N/A'
const intValue = Math.floor(Number(salaryValue))
return `$${intValue.toLocaleString()}`
}
const getStatusLabel = (statusValue) => {
const statusMap = {
0: 'Applied',
1: 'Current',
2: 'Professional',
3: 'Rejected',
4: 'Resigned'
}
return statusMap[statusValue] || 'Applied'
}
const getStatusColor = (statusValue) => {
const colorMap = {
0: 'info',
1: 'primary',
2: 'success',
3: 'error',
4: 'warning'
}
return colorMap[statusValue] || 'grey'
}
const statusOptions = [
{ value: 0, title: 'Applied', color: 'info' },
{ value: 1, title: 'Current', color: 'primary' },
{ value: 2, title: 'Professional', color: 'success' },
{ value: 3, title: 'Rejected', color: 'error' },
{ value: 4, title: 'Resigned', color: 'warning' }
]
const getFieldIcon = (field) => {
const iconMap = {
name: 'tabler-user',
email: 'tabler-mail',
startDate: 'tabler-calendar',
salary: 'tabler-currency-dollar',
age: 'tabler-hourglass',
status: 'tabler-badge'
}
return iconMap[field] || 'tabler-info-circle'
}
</script>
<template>
<v-dialog :model-value="modelValue" @update:model-value="closeDialog" :max-width="isMobile ? '95%' : '700px'" persistent>
<v-card
class="elevation-12"
rounded="lg"
:style="{
background: 'rgba(var(--v-theme-surface), 0.15)',
backdropFilter: 'blur(20px)',
webkitBackdropFilter: 'blur(20px)',
border: '1px solid rgba(var(--v-theme-outline), 0.2)',
boxShadow: '0 25px 45px rgba(0,0,0,0.1)'
}"
>
<v-card-title class="bg-primary text-white pa-4">
<div class="d-flex justify-space-between align-center w-100">
<div class="d-flex align-center">
<v-icon icon="tabler-user-circle" size="28" class="me-3" />
<span class="text-h5">{{ isEditMode ? 'Edit User' : 'User Details' }}</span>
</div>
<div class="d-flex align-center gap-2">
<template v-if="!isEditMode">
<v-btn
icon="tabler-edit"
variant="text"
size="small"
color="white"
:disabled="!selectedRowData"
@click="toggleEditMode"
title="Edit"
/>
<v-btn
icon="tabler-x"
variant="text"
size="small"
color="white"
@click="closeDialog"
title="Close"
/>
</template>
<template v-else>
<v-btn
icon="tabler-x"
variant="text"
size="small"
color="white"
@click="cancelEdit"
title="Cancel"
/>
<v-btn
icon="tabler-check"
variant="text"
size="small"
color="white"
@click="saveChanges"
title="Save"
/>
</template>
</div>
</div>
</v-card-title>
<v-card-text class="pa-6" v-if="selectedRowData">
<v-form @keyup.enter="handleEnterKey">
<v-container fluid class="pa-0">
<v-row>
<v-col cols="12" sm="6" class="mb-2">
<v-card variant="outlined" class="pa-4 h-100" color="surface-variant">
<div class="d-flex align-center">
<v-avatar
class="me-4"
size="40"
:style="{
background: 'rgba(var(--v-theme-primary), 0.2)',
backdropFilter: 'blur(10px)',
webkitBackdropFilter: 'blur(10px)',
border: '1px solid rgba(var(--v-theme-primary), 0.3)'
}"
>
<v-icon :icon="getFieldIcon('name')" color="primary" />
</v-avatar>
<div class="flex-grow-1">
<v-card-subtitle class="pa-0 text-caption text-medium-emphasis">Full Name</v-card-subtitle>
<v-card-title v-if="!isEditMode" class="pa-0 text-h6 text-high-emphasis">
{{ selectedRowData.fullName || 'N/A' }}
</v-card-title>
<v-text-field
v-else
v-model="editedData.fullName"
variant="outlined"
density="compact"
hide-details
class="mt-1"
/>
</div>
</div>
</v-card>
</v-col>
<v-col cols="12" sm="6" class="mb-2">
<v-card variant="outlined" class="pa-4 h-100" color="surface-variant">
<div class="d-flex align-center">
<v-avatar
class="me-4"
size="40"
:style="{
background: 'rgba(var(--v-theme-secondary), 0.2)',
backdropFilter: 'blur(10px)',
webkitBackdropFilter: 'blur(10px)',
border: '1px solid rgba(var(--v-theme-secondary), 0.3)'
}"
>
<v-icon :icon="getFieldIcon('email')" color="secondary" />
</v-avatar>
<div class="flex-grow-1">
<v-card-subtitle class="pa-0 text-caption text-medium-emphasis">Email Address</v-card-subtitle>
<v-card-title v-if="!isEditMode" class="pa-0 text-body-1 text-high-emphasis">
{{ selectedRowData.email || 'N/A' }}
</v-card-title>
<v-text-field
v-else
v-model="editedData.email"
variant="outlined"
density="compact"
hide-details
type="email"
class="mt-1"
/>
</div>
</div>
</v-card>
</v-col>
<v-col cols="12" sm="6" class="mb-2">
<v-card variant="outlined" class="pa-4 h-100" color="surface-variant">
<div class="d-flex align-center">
<v-avatar
class="me-4"
size="40"
:style="{
background: 'rgba(var(--v-theme-info), 0.2)',
backdropFilter: 'blur(10px)',
webkitBackdropFilter: 'blur(10px)',
border: '1px solid rgba(var(--v-theme-info), 0.3)'
}"
>
<v-icon :icon="getFieldIcon('startDate')" color="info" />
</v-avatar>
<div class="flex-grow-1">
<v-card-subtitle class="pa-0 text-caption text-medium-emphasis">Start Date</v-card-subtitle>
<v-card-title v-if="!isEditMode" class="pa-0 text-h6 text-high-emphasis">
{{ formatDate(selectedRowData.startDate) }}
</v-card-title>
<v-text-field
v-else
v-model="editedData.startDate"
variant="outlined"
density="compact"
hide-details
type="date"
class="mt-1"
/>
</div>
</div>
</v-card>
</v-col>
<v-col cols="12" sm="6" class="mb-2">
<v-card variant="outlined" class="pa-4 h-100" color="surface-variant">
<div class="d-flex align-center">
<v-avatar
class="me-4"
size="40"
:style="{
background: 'rgba(var(--v-theme-success), 0.2)',
backdropFilter: 'blur(10px)',
webkitBackdropFilter: 'blur(10px)',
border: '1px solid rgba(var(--v-theme-success), 0.3)'
}"
>
<v-icon :icon="getFieldIcon('salary')" color="success" />
</v-avatar>
<div class="flex-grow-1">
<v-card-subtitle class="pa-0 text-caption text-medium-emphasis">Salary</v-card-subtitle>
<v-card-title v-if="!isEditMode" class="pa-0 text-h6 text-success font-weight-bold">
{{ formatSalary(selectedRowData.salary) }}
</v-card-title>
<v-text-field
v-else
v-model="editedData.salary"
variant="outlined"
density="compact"
hide-details
type="number"
prefix="$"
class="mt-1"
/>
</div>
</div>
</v-card>
</v-col>
<v-col cols="12" sm="6" class="mb-2">
<v-card variant="outlined" class="pa-4 h-100" color="surface-variant">
<div class="d-flex align-center">
<v-avatar
class="me-4"
size="40"
:style="{
background: 'rgba(var(--v-theme-warning), 0.2)',
backdropFilter: 'blur(10px)',
webkitBackdropFilter: 'blur(10px)',
border: '1px solid rgba(var(--v-theme-warning), 0.3)'
}"
>
<v-icon :icon="getFieldIcon('age')" color="warning" />
</v-avatar>
<div class="flex-grow-1">
<v-card-subtitle class="pa-0 text-caption text-medium-emphasis">Age</v-card-subtitle>
<v-card-title v-if="!isEditMode" class="pa-0 text-h6 text-high-emphasis">
{{ selectedRowData.age ? selectedRowData.age + ' years' : 'N/A' }}
</v-card-title>
<v-text-field
v-else
v-model="editedData.age"
variant="outlined"
density="compact"
hide-details
type="number"
suffix="years"
class="mt-1"
/>
</div>
</div>
</v-card>
</v-col>
<v-col cols="12" sm="6" class="mb-2">
<v-card variant="outlined" class="pa-4 h-100" color="surface-variant">
<div class="d-flex align-center">
<v-avatar
class="me-4"
size="40"
:style="{
background: `rgba(var(--v-theme-${getStatusColor(isEditMode ? editedData.status : selectedRowData.status)}), 0.2)`,
backdropFilter: 'blur(10px)',
webkitBackdropFilter: 'blur(10px)',
border: `1px solid rgba(var(--v-theme-${getStatusColor(isEditMode ? editedData.status : selectedRowData.status)}), 0.3)`
}"
>
<v-icon :icon="getFieldIcon('status')" :color="getStatusColor(isEditMode ? editedData.status : selectedRowData.status)" />
</v-avatar>
<div class="flex-grow-1">
<v-card-subtitle class="pa-0 text-caption text-medium-emphasis">Status</v-card-subtitle>
<div v-if="!isEditMode" class="pt-1">
<v-chip
:color="getStatusColor(selectedRowData.status)"
size="default"
variant="flat"
class="font-weight-medium"
>
<v-icon start icon="tabler-check" size="small" />
{{ getStatusLabel(selectedRowData.status) }}
</v-chip>
</div>
<v-select
v-else
v-model="editedData.status"
:items="statusOptions"
variant="outlined"
density="compact"
hide-details
class="mt-1"
>
<template #item="{ props, item }">
<v-list-item v-bind="props" :title="item.title">
<template #prepend>
<v-chip :color="item.raw.color" size="small" class="me-2">{{ item.title }}</v-chip>
</template>
</v-list-item>
</template>
<template #selection="{ item }">
<v-chip :color="item.raw.color" size="small">{{ item.title }}</v-chip>
</template>
</v-select>
</div>
</div>
</v-card>
</v-col>
</v-row>
<v-row class="mt-4">
<v-col cols="12">
<v-alert type="info" variant="tonal" class="mb-0" icon="tabler-info-circle">
<v-alert-title class="text-body-1 font-weight-medium">Summary Information</v-alert-title>
<div class="mt-2 text-body-2">
Employee
<strong>{{ (isEditMode ? editedData.fullName : selectedRowData.fullName) || 'Unknown' }}</strong>
{{ (isEditMode ? editedData.age : selectedRowData.age) ? `(${isEditMode ? editedData.age : selectedRowData.age} years old)` : '' }}
has been with the company since
<strong>{{ formatDate(isEditMode ? editedData.startDate : selectedRowData.startDate) }}</strong>
with a current status of
<strong>{{ getStatusLabel(isEditMode ? editedData.status : selectedRowData.status) }}</strong>.
</div>
</v-alert>
</v-col>
</v-row>
</v-container>
</v-form>
</v-card-text>
<v-divider />
</v-card>
</v-dialog>
</template>