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:
@@ -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">
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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'
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -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 ? 'Отправка...' : 'Отправить заявку' }}
|
||||||
|
|||||||
@@ -14,4 +14,23 @@ export interface LoginCredentials {
|
|||||||
export interface AdminCredentials {
|
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
17
src/types/employee.ts
Normal 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
14
src/types/request.ts
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user