1
0
mirror of https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git synced 2025-08-14 00:25:46 +02:00

Change code, for build Docker

This commit is contained in:
MoonTestUse1
2024-12-23 22:03:04 +06:00
parent b6608572f2
commit 33c6806c1d
12 changed files with 89 additions and 29 deletions

View File

@@ -4,7 +4,7 @@
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div> <div>
<h3 class="text-sm font-semibold mb-2 flex items-center gap-2"> <h3 class="text-sm font-semibold mb-2 flex items-center gap-2">
<PhoneIcon size="16" class="text-blue-400" /> <PhoneIcon :size="16" class="text-blue-400" />
Контактная информация Контактная информация
</h3> </h3>
<div class="space-y-1 text-sm text-slate-300"> <div class="space-y-1 text-sm text-slate-300">
@@ -14,7 +14,7 @@
</div> </div>
<div> <div>
<h3 class="text-sm font-semibold mb-2 flex items-center gap-2"> <h3 class="text-sm font-semibold mb-2 flex items-center gap-2">
<ClockIcon size="16" class="text-blue-400" /> <ClockIcon :size="16" class="text-blue-400" />
Режим работы Режим работы
</h3> </h3>
<div class="space-y-1 text-sm text-slate-300"> <div class="space-y-1 text-sm text-slate-300">
@@ -24,7 +24,7 @@
</div> </div>
<div> <div>
<h3 class="text-sm font-semibold mb-2 flex items-center gap-2"> <h3 class="text-sm font-semibold mb-2 flex items-center gap-2">
<MailIcon size="16" class="text-blue-400" /> <MailIcon :size="16" class="text-blue-400" />
Техподдержка Техподдержка
</h3> </h3>
<div class="space-y-1 text-sm text-slate-300"> <div class="space-y-1 text-sm text-slate-300">

View File

@@ -3,14 +3,14 @@
<div class="container mx-auto px-4"> <div class="container mx-auto px-4">
<div class="flex flex-col sm:flex-row justify-between items-center gap-2"> <div class="flex flex-col sm:flex-row justify-between items-center gap-2">
<div class="flex items-center space-x-3"> <div class="flex items-center space-x-3">
<Building2Icon size="28" class="text-blue-400" /> <Building2Icon :size="28" class="text-blue-400" />
<div class="text-center sm:text-left"> <div class="text-center sm:text-left">
<h1 class="text-lg sm:text-xl font-semibold">Администрация КАО</h1> <h1 class="text-lg sm:text-xl font-semibold">Администрация КАО</h1>
<p class="text-xs sm:text-sm text-slate-300">Портал технической поддержки</p> <p class="text-xs sm:text-sm text-slate-300">Портал технической поддержки</p>
</div> </div>
</div> </div>
<div class="flex items-center space-x-2"> <div class="flex items-center space-x-2">
<PhoneIcon size="18" class="text-blue-400" /> <PhoneIcon :size="18" class="text-blue-400" />
<div class="text-center sm:text-left"> <div class="text-center sm:text-left">
<p class="text-xs text-slate-300">Поддержка:</p> <p class="text-xs text-slate-300">Поддержка:</p>
<p class="text-sm font-semibold">8 (800) 123-45-67</p> <p class="text-sm font-semibold">8 (800) 123-45-67</p>

View File

@@ -6,7 +6,7 @@
<div class="p-2 bg-blue-50 rounded-lg"> <div class="p-2 bg-blue-50 rounded-lg">
<component <component
:is="employee ? UserIcon : UserPlusIcon" :is="employee ? UserIcon : UserPlusIcon"
size="24" :size="24"
class="text-blue-600" class="text-blue-600"
/> />
</div> </div>
@@ -18,7 +18,7 @@
@click="$emit('close')" @click="$emit('close')"
class="text-gray-400 hover:text-gray-500 transition-colors" class="text-gray-400 hover:text-gray-500 transition-colors"
> >
<XIcon size="20" /> <XIcon :size="20" />
</button> </button>
</div> </div>
@@ -30,7 +30,7 @@
</label> </label>
<div class="relative"> <div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<UserIcon size="18" class="text-gray-400" /> <UserIcon :size="18" class="text-gray-400" />
</div> </div>
<input <input
v-model="formData.last_name" v-model="formData.last_name"
@@ -48,7 +48,7 @@
</label> </label>
<div class="relative"> <div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<UserIcon size="18" class="text-gray-400" /> <UserIcon :size="18" class="text-gray-400" />
</div> </div>
<input <input
v-model="formData.first_name" v-model="formData.first_name"
@@ -66,7 +66,7 @@
</label> </label>
<div class="relative"> <div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<BuildingIcon size="18" class="text-gray-400" /> <BuildingIcon :size="18" class="text-gray-400" />
</div> </div>
<select <select
v-model="formData.department" v-model="formData.department"
@@ -87,7 +87,7 @@
</label> </label>
<div class="relative"> <div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<DoorClosedIcon size="18" class="text-gray-400" /> <DoorClosedIcon :size="18" class="text-gray-400" />
</div> </div>
<input <input
v-model="formData.office" v-model="formData.office"
@@ -105,7 +105,7 @@
</label> </label>
<div class="relative"> <div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<LockIcon size="18" class="text-gray-400" /> <LockIcon :size="18" class="text-gray-400" />
</div> </div>
<input <input
v-model="formData.password" v-model="formData.password"
@@ -127,14 +127,14 @@
@click="$emit('close')" @click="$emit('close')"
class="px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors flex items-center gap-2" class="px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors flex items-center gap-2"
> >
<XIcon size="16" /> <XIcon :size="16" />
Отмена Отмена
</button> </button>
<button <button
type="submit" type="submit"
class="px-4 py-2 bg-blue-600 text-white rounded-md text-sm font-medium hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors flex items-center gap-2" class="px-4 py-2 bg-blue-600 text-white rounded-md text-sm font-medium hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-colors flex items-center gap-2"
> >
<component :is="employee ? SaveIcon : UserPlusIcon" size="16" /> <component :is="employee ? SaveIcon : UserPlusIcon" :size="16" />
{{ employee ? 'Сохранить' : 'Добавить' }} {{ employee ? 'Сохранить' : 'Добавить' }}
</button> </button>
</div> </div>
@@ -147,6 +147,8 @@
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { XIcon, UserIcon, BuildingIcon, DoorClosedIcon, LockIcon, UserPlusIcon, SaveIcon } from 'lucide-vue-next'; import { XIcon, UserIcon, BuildingIcon, DoorClosedIcon, LockIcon, UserPlusIcon, SaveIcon } from 'lucide-vue-next';
import { departments } from '@/utils/constants'; import { departments } from '@/utils/constants';
import type { EmployeeFormData } from '@/types/employee';
const props = defineProps<{ const props = defineProps<{
employee?: any; employee?: any;
@@ -157,7 +159,7 @@ const emit = defineEmits<{
(e: 'submit', data: any): void; (e: 'submit', data: any): void;
}>(); }>();
const formData = ref({ const formData = ref<EmployeeFormData>({
first_name: '', first_name: '',
last_name: '', last_name: '',
department: '', department: '',
@@ -165,6 +167,7 @@ const formData = ref({
password: '' password: ''
}); });
onMounted(() => { onMounted(() => {
if (props.employee) { if (props.employee) {
formData.value = { formData.value = {
@@ -180,7 +183,7 @@ onMounted(() => {
function handleSubmit() { function handleSubmit() {
const data = { ...formData.value }; const data = { ...formData.value };
if (props.employee && !data.password) { if (props.employee && !data.password) {
delete data.password; delete data.password; // Теперь это безопасно, так как password опциональный
} }
emit('submit', data); emit('submit', data);
} }

View File

@@ -1,12 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { UserPlusIcon, PencilIcon } from 'lucide-vue-next';
import { departments } from '@/utils/constants'; import { departments } from '@/utils/constants';
import EmployeeForm from './EmployeeForm.vue'; import EmployeeFormModal from './employee/EmployeeFormModal.vue';
import Notification from '@/components/ui/Notification.vue';
import type { Employee } from '@/types/employee';
const employees = ref([]); const employees = ref<Employee[]>([]); // Добавляем типизацию массива сотрудников
const showAddForm = ref(false); const showAddForm = ref(false);
const editingEmployee = ref(null); const editingEmployee = ref<Employee | null>(null);
const showNotification = ref(false);
const notificationMessage = ref('');
function getDepartmentLabel(value: string) { function getDepartmentLabel(value: string) {
return departments.find(d => d.value === value)?.label || value; return departments.find(d => d.value === value)?.label || value;

View File

@@ -7,7 +7,7 @@
@click="$emit('close')" @click="$emit('close')"
class="text-gray-400 hover:text-gray-500" class="text-gray-400 hover:text-gray-500"
> >
<XIcon size="20" /> <XIcon :size="20" />
</button> </button>
</div> </div>

View File

@@ -105,16 +105,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import type { Request } from '@/types/request';
import RequestStatusBadge from './RequestStatusBadge.vue'; import RequestStatusBadge from './RequestStatusBadge.vue';
import RequestPriorityBadge from './RequestPriorityBadge.vue'; import RequestPriorityBadge from './RequestPriorityBadge.vue';
import RequestStatusModal from './RequestStatusModal.vue'; import RequestStatusModal from './RequestStatusModal.vue';
import RequestDescriptionModal from './RequestDescriptionModal.vue'; import RequestDescriptionModal from './RequestDescriptionModal.vue';
import Notification from '@/components/ui/Notification.vue'; import Notification from '@/components/ui/Notification.vue';
import { getRequestTypeLabel } from '@/utils/constants'; import { getRequestTypeLabel } from '@/utils/constants';
const requests = ref<Request[]>([]); // Типизируем массив запросов
const requests = ref([]); const selectedRequest = ref<Request | null>(null);
const selectedRequest = ref(null); const selectedDescription = ref<Request | null>(null);
const selectedDescription = ref(null);
const showNotification = ref(false); const showNotification = ref(false);
const filter = ref('all'); const filter = ref('all');
const searchQuery = ref(''); const searchQuery = ref('');
@@ -135,6 +135,7 @@ const filteredRequests = computed(() => {
return result; return result;
}); });
function formatDate(date: string) { function formatDate(date: string) {
return new Date(date).toLocaleString('ru-RU'); return new Date(date).toLocaleString('ru-RU');
} }

View File

@@ -8,7 +8,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
const props = defineProps<{ const { priority } = defineProps<{
priority: 'low' | 'medium' | 'high' | 'critical' priority: 'low' | 'medium' | 'high' | 'critical'
}>(); }>();

View File

@@ -46,16 +46,17 @@
<script setup lang="ts"> <script setup lang="ts">
import RequestStatusBadge from './RequestStatusBadge.vue'; import RequestStatusBadge from './RequestStatusBadge.vue';
import type { RequestStatus } from '@/types/request';
const props = defineProps<{ const props = defineProps<{
currentStatus: string; currentStatus: RequestStatus;
}>(); }>();
const emit = defineEmits(['close', 'update']); const emit = defineEmits(['close', 'update']);
const allStatuses = ['new', 'in_progress', 'resolved', 'closed'] as const; const allStatuses: RequestStatus[] = ['new', 'in_progress', 'resolved', 'closed'];
function handleStatusSelect(newStatus: string) { function handleStatusSelect(newStatus: RequestStatus) {
if (newStatus === props.currentStatus) return; if (newStatus === props.currentStatus) return;
emit('update', newStatus); emit('update', newStatus);
emit('close'); emit('close');

View File

@@ -55,7 +55,7 @@
class="w-full bg-blue-600 text-white py-2 px-4 text-sm sm:text-base rounded-md hover:bg-blue-700 transition-colors flex items-center justify-center gap-2 disabled:opacity-50" class="w-full bg-blue-600 text-white py-2 px-4 text-sm sm:text-base rounded-md hover:bg-blue-700 transition-colors flex items-center justify-center gap-2 disabled:opacity-50"
> >
<component :is="isSubmitting ? LoaderIcon : SendIcon" <component :is="isSubmitting ? LoaderIcon : SendIcon"
size="18" :size="18"
:class="{ 'animate-spin': isSubmitting }" :class="{ 'animate-spin': isSubmitting }"
/> />
{{ isSubmitting ? 'Отправка...' : 'Отправить заявку' }} {{ isSubmitting ? 'Отправка...' : 'Отправить заявку' }}

View File

@@ -15,3 +15,22 @@ export interface AdminCredentials {
username: string; username: string;
password: string; password: string;
} }
export interface Employee {
id: number;
first_name: string;
last_name: string;
department: string;
office: string;
}
export interface Request {
id: number;
employee_last_name: string;
employee_first_name: string;
employee_office: string;
request_type: string;
priority: 'low' | 'medium' | 'high' | 'critical';
status: 'new' | 'in_progress' | 'resolved' | 'closed';
created_at: string;
}

17
src/types/employee.ts Normal file
View File

@@ -0,0 +1,17 @@
export interface Employee {
id: number;
first_name: string;
last_name: string;
department: string;
office: string;
created_at?: string;
}
export interface EmployeeFormData {
first_name: string;
last_name: string;
department: string;
office: string;
password?: string; // Делаем password опциональным
}

14
src/types/request.ts Normal file
View File

@@ -0,0 +1,14 @@
export type RequestStatus = 'new' | 'in_progress' | 'resolved' | 'closed';
export interface Request {
id: number;
status: RequestStatus;
created_at: string;
employee_last_name: string;
employee_first_name: string;
employee_office: string;
request_type: string;
priority: 'low' | 'medium' | 'high' | 'critical';
description: string;
}