Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5233db2002 | ||
|
|
dac45baaa8 | ||
|
|
f389c4b3cf | ||
|
|
fdc7037e60 | ||
|
|
b02f6afe93 | ||
|
|
1b2ab7b4eb | ||
|
|
29975ecad9 | ||
|
|
e846f15126 | ||
|
|
86bebf9776 | ||
|
|
7b7c6bde68 | ||
|
|
935fb235a6 | ||
|
|
d2d1cdbb92 | ||
|
|
5f08b0e086 | ||
|
|
637499a8ec | ||
|
|
f413bae348 | ||
|
|
2b1de583fd | ||
|
|
5500cdb118 | ||
|
|
2254a13ad0 | ||
|
|
1f5885de84 | ||
|
|
705f58e3d1 | ||
|
|
0330453ad6 | ||
|
|
66b98e2765 | ||
|
|
55648c9a30 |
@@ -1,43 +0,0 @@
|
|||||||
---
|
|
||||||
name: compare-php
|
|
||||||
description: Compare a feature between PHP boha-app and TS boha-app-ts to verify migration parity
|
|
||||||
---
|
|
||||||
|
|
||||||
# Compare PHP vs TS Implementation
|
|
||||||
|
|
||||||
Compare a specific feature, component, or endpoint between the original PHP project (D:\cortex\boha-app) and the TypeScript migration (D:\cortex\boha-app-ts).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
The user will specify what to compare. Examples:
|
|
||||||
- `/compare-php attendance print`
|
|
||||||
- `/compare-php invoice PDF generation`
|
|
||||||
- `/compare-php offer numbering logic`
|
|
||||||
|
|
||||||
## Process
|
|
||||||
|
|
||||||
1. Search the PHP codebase (D:\cortex\boha-app) for the relevant implementation
|
|
||||||
2. Search the TS codebase (D:\cortex\boha-app-ts) for the same feature
|
|
||||||
3. Compare:
|
|
||||||
- API endpoints and request/response shape
|
|
||||||
- Business logic and calculations
|
|
||||||
- Database queries
|
|
||||||
- Frontend behavior
|
|
||||||
4. Report differences found — what's missing, what's different, what's extra
|
|
||||||
|
|
||||||
## Key directories
|
|
||||||
|
|
||||||
**PHP project:**
|
|
||||||
- API handlers: `D:\cortex\boha-app\api\admin\handlers\`
|
|
||||||
- API routes: `D:\cortex\boha-app\api\admin\`
|
|
||||||
- Frontend pages: `D:\cortex\boha-app\src\admin\pages\`
|
|
||||||
- Frontend components: `D:\cortex\boha-app\src\admin\components\`
|
|
||||||
- Frontend hooks: `D:\cortex\boha-app\src\admin\hooks\`
|
|
||||||
- Includes/utils: `D:\cortex\boha-app\api\includes\`
|
|
||||||
|
|
||||||
**TS project:**
|
|
||||||
- API routes: `D:\cortex\boha-app-ts\src\routes\admin\`
|
|
||||||
- Services: `D:\cortex\boha-app-ts\src\services\`
|
|
||||||
- Frontend pages: `D:\cortex\boha-app-ts\src\admin\pages\`
|
|
||||||
- Frontend components: `D:\cortex\boha-app-ts\src\admin\components\`
|
|
||||||
- Frontend hooks: `D:\cortex\boha-app-ts\src\admin\hooks\`
|
|
||||||
@@ -87,6 +87,8 @@ npx prisma migrate resolve --applied <migration> # Mark migration as applied
|
|||||||
|
|
||||||
**Do not start the dev server.** The user manages it separately.
|
**Do not start the dev server.** The user manages it separately.
|
||||||
|
|
||||||
|
**Before running `prisma migrate dev` or `prisma db push`, ask the user to stop their dev server and wait for confirmation.** Migrations can conflict with an active database connection from the running server.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|||||||
572
package-lock.json
generated
572
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "app-ts",
|
"name": "app-ts",
|
||||||
"version": "1.6.5",
|
"version": "1.8.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "dist/server.js",
|
"main": "dist/server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -18,7 +18,6 @@
|
|||||||
"db:studio": "prisma studio",
|
"db:studio": "prisma studio",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"bones": "boneyard-js build http://localhost:3000",
|
|
||||||
"seed": "tsx prisma/seed.ts"
|
"seed": "tsx prisma/seed.ts"
|
||||||
},
|
},
|
||||||
"prisma": {
|
"prisma": {
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
"@tanstack/react-query": "^5.100.5",
|
"@tanstack/react-query": "^5.100.5",
|
||||||
"@types/jsdom": "^28.0.1",
|
"@types/jsdom": "^28.0.1",
|
||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
"boneyard-js": "^1.8.1",
|
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"dompurify": "^3.3.3",
|
"dompurify": "^3.3.3",
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Insert the settings.system permission (also available via seed)
|
||||||
|
INSERT INTO `permissions` (`name`, `display_name`, `module`, `description`)
|
||||||
|
VALUES ('settings.system', 'Systémová nastavení', 'settings', 'Spravovat systémová nastavení, 2FA, e-maily, limity')
|
||||||
|
ON DUPLICATE KEY UPDATE `name` = `name`;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `quotations` DROP COLUMN `exchange_rate`;
|
||||||
|
ALTER TABLE `quotations` DROP COLUMN `exchange_rate_date`;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `invoice_items` ADD COLUMN `item_description` TEXT NULL;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
-- AlterTable: quotation_items
|
||||||
|
ALTER TABLE `quotation_items` DROP COLUMN `uuid`;
|
||||||
|
ALTER TABLE `quotation_items` DROP COLUMN `is_deleted`;
|
||||||
|
ALTER TABLE `quotation_items` DROP COLUMN `sync_version`;
|
||||||
|
|
||||||
|
-- AlterTable: quotations
|
||||||
|
ALTER TABLE `quotations` DROP COLUMN `uuid`;
|
||||||
|
ALTER TABLE `quotations` DROP COLUMN `sync_version`;
|
||||||
|
|
||||||
|
-- AlterTable: scope_sections
|
||||||
|
ALTER TABLE `scope_sections` DROP COLUMN `content_editor_height`;
|
||||||
|
ALTER TABLE `scope_sections` DROP COLUMN `uuid`;
|
||||||
|
ALTER TABLE `scope_sections` DROP COLUMN `is_deleted`;
|
||||||
|
ALTER TABLE `scope_sections` DROP COLUMN `sync_version`;
|
||||||
@@ -0,0 +1,277 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_categories` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` VARCHAR(100) NOT NULL,
|
||||||
|
`description` TEXT NULL,
|
||||||
|
`sort_order` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
|
||||||
|
`modified_at` DATETIME(0) NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_suppliers` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` VARCHAR(255) NOT NULL,
|
||||||
|
`ico` VARCHAR(20) NULL,
|
||||||
|
`dic` VARCHAR(20) NULL,
|
||||||
|
`contact_person` VARCHAR(255) NULL,
|
||||||
|
`email` VARCHAR(255) NULL,
|
||||||
|
`phone` VARCHAR(50) NULL,
|
||||||
|
`address` TEXT NULL,
|
||||||
|
`notes` TEXT NULL,
|
||||||
|
`is_active` BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
|
||||||
|
`modified_at` DATETIME(0) NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_locations` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`code` VARCHAR(20) NOT NULL,
|
||||||
|
`name` VARCHAR(100) NOT NULL,
|
||||||
|
`description` TEXT NULL,
|
||||||
|
`is_active` BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
|
||||||
|
`modified_at` DATETIME(0) NULL,
|
||||||
|
|
||||||
|
UNIQUE INDEX `sklad_locations_code_key`(`code`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_items` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`item_number` VARCHAR(50) NULL,
|
||||||
|
`name` VARCHAR(255) NOT NULL,
|
||||||
|
`description` TEXT NULL,
|
||||||
|
`category_id` INTEGER NULL,
|
||||||
|
`unit` VARCHAR(20) NOT NULL,
|
||||||
|
`min_quantity` DECIMAL(12, 3) NULL,
|
||||||
|
`is_active` BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
`notes` TEXT NULL,
|
||||||
|
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
|
||||||
|
`modified_at` DATETIME(0) NULL,
|
||||||
|
|
||||||
|
UNIQUE INDEX `sklad_items_item_number_key`(`item_number`),
|
||||||
|
INDEX `sklad_items_category_id`(`category_id`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_batches` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`item_id` INTEGER NOT NULL,
|
||||||
|
`receipt_line_id` INTEGER NOT NULL,
|
||||||
|
`quantity` DECIMAL(12, 3) NOT NULL,
|
||||||
|
`original_qty` DECIMAL(12, 3) NOT NULL,
|
||||||
|
`unit_price` DECIMAL(12, 2) NOT NULL,
|
||||||
|
`received_at` DATETIME(0) NULL,
|
||||||
|
`is_consumed` BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
|
UNIQUE INDEX `sklad_batches_receipt_line_id_key`(`receipt_line_id`),
|
||||||
|
INDEX `sklad_batches_fifo`(`item_id`, `is_consumed`, `received_at`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_item_locations` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`item_id` INTEGER NOT NULL,
|
||||||
|
`location_id` INTEGER NOT NULL,
|
||||||
|
`quantity` DECIMAL(12, 3) NOT NULL DEFAULT 0,
|
||||||
|
|
||||||
|
UNIQUE INDEX `sklad_item_locations_unique`(`item_id`, `location_id`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_receipts` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`receipt_number` VARCHAR(50) NULL,
|
||||||
|
`supplier_id` INTEGER NULL,
|
||||||
|
`delivery_note_number` VARCHAR(100) NULL,
|
||||||
|
`delivery_note_date` DATETIME(0) NULL,
|
||||||
|
`received_by` INTEGER NULL,
|
||||||
|
`notes` TEXT NULL,
|
||||||
|
`status` ENUM('DRAFT', 'CONFIRMED', 'CANCELLED') NOT NULL DEFAULT 'DRAFT',
|
||||||
|
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
|
||||||
|
`modified_at` DATETIME(0) NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_receipt_lines` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`receipt_id` INTEGER NOT NULL,
|
||||||
|
`item_id` INTEGER NOT NULL,
|
||||||
|
`quantity` DECIMAL(12, 3) NOT NULL,
|
||||||
|
`unit_price` DECIMAL(12, 2) NOT NULL,
|
||||||
|
`location_id` INTEGER NULL,
|
||||||
|
`notes` VARCHAR(255) NULL,
|
||||||
|
|
||||||
|
INDEX `sklad_receipt_lines_receipt_id`(`receipt_id`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_receipt_attachments` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`receipt_id` INTEGER NOT NULL,
|
||||||
|
`file_name` VARCHAR(255) NOT NULL,
|
||||||
|
`file_mime` VARCHAR(100) NOT NULL,
|
||||||
|
`file_size` INTEGER NOT NULL,
|
||||||
|
`file_path` VARCHAR(500) NOT NULL,
|
||||||
|
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_issues` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`issue_number` VARCHAR(50) NULL,
|
||||||
|
`project_id` INTEGER NOT NULL,
|
||||||
|
`issued_by` INTEGER NULL,
|
||||||
|
`notes` TEXT NULL,
|
||||||
|
`status` ENUM('DRAFT', 'CONFIRMED', 'CANCELLED') NOT NULL DEFAULT 'DRAFT',
|
||||||
|
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
|
||||||
|
`modified_at` DATETIME(0) NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_issue_lines` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`issue_id` INTEGER NOT NULL,
|
||||||
|
`item_id` INTEGER NOT NULL,
|
||||||
|
`batch_id` INTEGER NOT NULL,
|
||||||
|
`quantity` DECIMAL(12, 3) NOT NULL,
|
||||||
|
`location_id` INTEGER NULL,
|
||||||
|
`reservation_id` INTEGER NULL,
|
||||||
|
`notes` VARCHAR(255) NULL,
|
||||||
|
|
||||||
|
INDEX `sklad_issue_lines_issue_id`(`issue_id`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_reservations` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`item_id` INTEGER NOT NULL,
|
||||||
|
`project_id` INTEGER NOT NULL,
|
||||||
|
`quantity` DECIMAL(12, 3) NOT NULL,
|
||||||
|
`remaining_qty` DECIMAL(12, 3) NOT NULL,
|
||||||
|
`reserved_by` INTEGER NULL,
|
||||||
|
`notes` TEXT NULL,
|
||||||
|
`status` ENUM('ACTIVE', 'FULFILLED', 'CANCELLED') NOT NULL DEFAULT 'ACTIVE',
|
||||||
|
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
|
||||||
|
`modified_at` DATETIME(0) NULL,
|
||||||
|
|
||||||
|
INDEX `sklad_reservations_item_status`(`item_id`, `status`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_inventory_sessions` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`session_number` VARCHAR(50) NULL,
|
||||||
|
`notes` TEXT NULL,
|
||||||
|
`status` ENUM('DRAFT', 'CONFIRMED') NOT NULL DEFAULT 'DRAFT',
|
||||||
|
`created_at` DATETIME(0) NULL DEFAULT CURRENT_TIMESTAMP(0),
|
||||||
|
`modified_at` DATETIME(0) NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `sklad_inventory_lines` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`session_id` INTEGER NOT NULL,
|
||||||
|
`item_id` INTEGER NOT NULL,
|
||||||
|
`location_id` INTEGER NULL,
|
||||||
|
`system_qty` DECIMAL(12, 3) NOT NULL,
|
||||||
|
`actual_qty` DECIMAL(12, 3) NOT NULL,
|
||||||
|
`difference` DECIMAL(12, 3) NOT NULL,
|
||||||
|
`notes` VARCHAR(255) NULL,
|
||||||
|
|
||||||
|
INDEX `sklad_inventory_lines_session_id`(`session_id`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_items` ADD CONSTRAINT `sklad_items_category_id_fkey` FOREIGN KEY (`category_id`) REFERENCES `sklad_categories`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_batches` ADD CONSTRAINT `sklad_batches_item_id_fkey` FOREIGN KEY (`item_id`) REFERENCES `sklad_items`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_batches` ADD CONSTRAINT `sklad_batches_receipt_line_id_fkey` FOREIGN KEY (`receipt_line_id`) REFERENCES `sklad_receipt_lines`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_item_locations` ADD CONSTRAINT `sklad_item_locations_item_id_fkey` FOREIGN KEY (`item_id`) REFERENCES `sklad_items`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_item_locations` ADD CONSTRAINT `sklad_item_locations_location_id_fkey` FOREIGN KEY (`location_id`) REFERENCES `sklad_locations`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_receipts` ADD CONSTRAINT `sklad_receipts_supplier_id_fkey` FOREIGN KEY (`supplier_id`) REFERENCES `sklad_suppliers`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_receipts` ADD CONSTRAINT `sklad_receipts_received_by_fkey` FOREIGN KEY (`received_by`) REFERENCES `users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_receipt_lines` ADD CONSTRAINT `sklad_receipt_lines_receipt_id_fkey` FOREIGN KEY (`receipt_id`) REFERENCES `sklad_receipts`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_receipt_lines` ADD CONSTRAINT `sklad_receipt_lines_item_id_fkey` FOREIGN KEY (`item_id`) REFERENCES `sklad_items`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_receipt_lines` ADD CONSTRAINT `sklad_receipt_lines_location_id_fkey` FOREIGN KEY (`location_id`) REFERENCES `sklad_locations`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_receipt_attachments` ADD CONSTRAINT `sklad_receipt_attachments_receipt_id_fkey` FOREIGN KEY (`receipt_id`) REFERENCES `sklad_receipts`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_issues` ADD CONSTRAINT `sklad_issues_project_id_fkey` FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_issues` ADD CONSTRAINT `sklad_issues_issued_by_fkey` FOREIGN KEY (`issued_by`) REFERENCES `users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_issue_lines` ADD CONSTRAINT `sklad_issue_lines_issue_id_fkey` FOREIGN KEY (`issue_id`) REFERENCES `sklad_issues`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_issue_lines` ADD CONSTRAINT `sklad_issue_lines_item_id_fkey` FOREIGN KEY (`item_id`) REFERENCES `sklad_items`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_issue_lines` ADD CONSTRAINT `sklad_issue_lines_batch_id_fkey` FOREIGN KEY (`batch_id`) REFERENCES `sklad_batches`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_issue_lines` ADD CONSTRAINT `sklad_issue_lines_location_id_fkey` FOREIGN KEY (`location_id`) REFERENCES `sklad_locations`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_issue_lines` ADD CONSTRAINT `sklad_issue_lines_reservation_id_fkey` FOREIGN KEY (`reservation_id`) REFERENCES `sklad_reservations`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_reservations` ADD CONSTRAINT `sklad_reservations_item_id_fkey` FOREIGN KEY (`item_id`) REFERENCES `sklad_items`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_reservations` ADD CONSTRAINT `sklad_reservations_project_id_fkey` FOREIGN KEY (`project_id`) REFERENCES `projects`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_reservations` ADD CONSTRAINT `sklad_reservations_reserved_by_fkey` FOREIGN KEY (`reserved_by`) REFERENCES `users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_inventory_lines` ADD CONSTRAINT `sklad_inventory_lines_session_id_fkey` FOREIGN KEY (`session_id`) REFERENCES `sklad_inventory_sessions`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_inventory_lines` ADD CONSTRAINT `sklad_inventory_lines_item_id_fkey` FOREIGN KEY (`item_id`) REFERENCES `sklad_items`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `sklad_inventory_lines` ADD CONSTRAINT `sklad_inventory_lines_location_id_fkey` FOREIGN KEY (`location_id`) REFERENCES `sklad_locations`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `company_settings` ADD COLUMN `warehouse_inventory_number_pattern` VARCHAR(100) NULL,
|
||||||
|
ADD COLUMN `warehouse_inventory_prefix` VARCHAR(20) NULL,
|
||||||
|
ADD COLUMN `warehouse_issue_number_pattern` VARCHAR(100) NULL,
|
||||||
|
ADD COLUMN `warehouse_issue_prefix` VARCHAR(20) NULL,
|
||||||
|
ADD COLUMN `warehouse_receipt_number_pattern` VARCHAR(100) NULL,
|
||||||
|
ADD COLUMN `warehouse_receipt_prefix` VARCHAR(20) NULL;
|
||||||
|
|
||||||
|
-- Set backward-compatible defaults for existing installations
|
||||||
|
UPDATE `company_settings`
|
||||||
|
SET
|
||||||
|
`warehouse_receipt_prefix` = 'PRI',
|
||||||
|
`warehouse_receipt_number_pattern` = '{PREFIX}-{YYYY}-{NNN}',
|
||||||
|
`warehouse_issue_prefix` = 'VYD',
|
||||||
|
`warehouse_issue_number_pattern` = '{PREFIX}-{YYYY}-{NNN}',
|
||||||
|
`warehouse_inventory_prefix` = 'INV',
|
||||||
|
`warehouse_inventory_number_pattern` = '{PREFIX}-{YYYY}-{NNN}';
|
||||||
@@ -128,6 +128,12 @@ model company_settings {
|
|||||||
offer_number_pattern String? @db.VarChar(100)
|
offer_number_pattern String? @db.VarChar(100)
|
||||||
order_number_pattern String? @db.VarChar(100)
|
order_number_pattern String? @db.VarChar(100)
|
||||||
invoice_number_pattern String? @db.VarChar(100)
|
invoice_number_pattern String? @db.VarChar(100)
|
||||||
|
warehouse_receipt_prefix String? @db.VarChar(20)
|
||||||
|
warehouse_receipt_number_pattern String? @db.VarChar(100)
|
||||||
|
warehouse_issue_prefix String? @db.VarChar(20)
|
||||||
|
warehouse_issue_number_pattern String? @db.VarChar(100)
|
||||||
|
warehouse_inventory_prefix String? @db.VarChar(20)
|
||||||
|
warehouse_inventory_number_pattern String? @db.VarChar(100)
|
||||||
}
|
}
|
||||||
|
|
||||||
model customers {
|
model customers {
|
||||||
@@ -153,8 +159,9 @@ model customers {
|
|||||||
model invoice_items {
|
model invoice_items {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
invoice_id Int
|
invoice_id Int
|
||||||
description String? @db.VarChar(500)
|
description String? @db.VarChar(500)
|
||||||
quantity Decimal? @default(1.000) @db.Decimal(12, 3)
|
item_description String? @db.Text
|
||||||
|
quantity Decimal? @default(1.000) @db.Decimal(12, 3)
|
||||||
unit String? @db.VarChar(20)
|
unit String? @db.VarChar(20)
|
||||||
unit_price Decimal? @default(0.00) @db.Decimal(12, 2)
|
unit_price Decimal? @default(0.00) @db.Decimal(12, 2)
|
||||||
vat_rate Decimal? @default(21.00) @db.Decimal(5, 2)
|
vat_rate Decimal? @default(21.00) @db.Decimal(5, 2)
|
||||||
@@ -356,6 +363,8 @@ model projects {
|
|||||||
modified_at DateTime? @db.DateTime(0)
|
modified_at DateTime? @db.DateTime(0)
|
||||||
attendance_project_logs attendance_project_logs[]
|
attendance_project_logs attendance_project_logs[]
|
||||||
project_notes project_notes[]
|
project_notes project_notes[]
|
||||||
|
sklad_issues sklad_issues[]
|
||||||
|
sklad_reservations sklad_reservations[]
|
||||||
users users? @relation(fields: [responsible_user_id], references: [id], onUpdate: NoAction, map: "fk_projects_responsible_user")
|
users users? @relation(fields: [responsible_user_id], references: [id], onUpdate: NoAction, map: "fk_projects_responsible_user")
|
||||||
customers customers? @relation(fields: [customer_id], references: [id], onUpdate: NoAction, map: "projects_ibfk_1")
|
customers customers? @relation(fields: [customer_id], references: [id], onUpdate: NoAction, map: "projects_ibfk_1")
|
||||||
quotations quotations? @relation(fields: [quotation_id], references: [id], onUpdate: NoAction, map: "projects_ibfk_2")
|
quotations quotations? @relation(fields: [quotation_id], references: [id], onUpdate: NoAction, map: "projects_ibfk_2")
|
||||||
@@ -377,10 +386,7 @@ model quotation_items {
|
|||||||
unit String? @db.VarChar(20)
|
unit String? @db.VarChar(20)
|
||||||
unit_price Decimal? @default(0.00) @db.Decimal(12, 2)
|
unit_price Decimal? @default(0.00) @db.Decimal(12, 2)
|
||||||
is_included_in_total Boolean? @default(true)
|
is_included_in_total Boolean? @default(true)
|
||||||
uuid String? @db.VarChar(36)
|
|
||||||
modified_at DateTime? @db.DateTime(0)
|
modified_at DateTime? @db.DateTime(0)
|
||||||
is_deleted Boolean? @default(false)
|
|
||||||
sync_version Int? @default(0)
|
|
||||||
quotations quotations @relation(fields: [quotation_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "quotation_items_ibfk_1")
|
quotations quotations @relation(fields: [quotation_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "quotation_items_ibfk_1")
|
||||||
|
|
||||||
@@index([quotation_id], map: "quotation_id")
|
@@index([quotation_id], map: "quotation_id")
|
||||||
@@ -397,17 +403,13 @@ model quotations {
|
|||||||
language String? @default("cs") @db.VarChar(5)
|
language String? @default("cs") @db.VarChar(5)
|
||||||
vat_rate Decimal? @default(21.00) @db.Decimal(5, 2)
|
vat_rate Decimal? @default(21.00) @db.Decimal(5, 2)
|
||||||
apply_vat Boolean? @default(true)
|
apply_vat Boolean? @default(true)
|
||||||
exchange_rate Decimal? @default(1.0000) @db.Decimal(10, 4)
|
|
||||||
exchange_rate_date DateTime? @db.Date
|
|
||||||
order_id Int?
|
order_id Int?
|
||||||
status String @default("active") @db.VarChar(20)
|
status String @default("active") @db.VarChar(20)
|
||||||
scope_title String? @db.VarChar(500)
|
scope_title String? @db.VarChar(500)
|
||||||
scope_description String? @db.Text
|
scope_description String? @db.Text
|
||||||
locked_by Int?
|
locked_by Int?
|
||||||
locked_at DateTime? @db.DateTime(0)
|
locked_at DateTime? @db.DateTime(0)
|
||||||
uuid String? @db.VarChar(36)
|
|
||||||
modified_at DateTime? @db.DateTime(0)
|
modified_at DateTime? @db.DateTime(0)
|
||||||
sync_version Int? @default(0)
|
|
||||||
orders orders[]
|
orders orders[]
|
||||||
projects projects[]
|
projects projects[]
|
||||||
quotation_items quotation_items[]
|
quotation_items quotation_items[]
|
||||||
@@ -492,11 +494,7 @@ model scope_sections {
|
|||||||
title String? @db.VarChar(500)
|
title String? @db.VarChar(500)
|
||||||
title_cz String? @db.VarChar(500)
|
title_cz String? @db.VarChar(500)
|
||||||
content String? @db.Text
|
content String? @db.Text
|
||||||
content_editor_height Int?
|
|
||||||
uuid String? @db.VarChar(36)
|
|
||||||
modified_at DateTime? @db.DateTime(0)
|
modified_at DateTime? @db.DateTime(0)
|
||||||
is_deleted Boolean? @default(false)
|
|
||||||
sync_version Int? @default(0)
|
|
||||||
quotations quotations @relation(fields: [quotation_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "scope_sections_ibfk_1")
|
quotations quotations @relation(fields: [quotation_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "scope_sections_ibfk_1")
|
||||||
|
|
||||||
@@index([quotation_id], map: "quotation_id")
|
@@index([quotation_id], map: "quotation_id")
|
||||||
@@ -590,6 +588,9 @@ model users {
|
|||||||
leave_requests_leave_requests_reviewer_idTousers leave_requests[] @relation("leave_requests_reviewer_idTousers")
|
leave_requests_leave_requests_reviewer_idTousers leave_requests[] @relation("leave_requests_reviewer_idTousers")
|
||||||
projects projects[]
|
projects projects[]
|
||||||
trips trips[]
|
trips trips[]
|
||||||
|
sklad_receipts_received sklad_receipts[] @relation("SkladReceivedBy")
|
||||||
|
sklad_issues_issued sklad_issues[] @relation("SkladIssuedBy")
|
||||||
|
sklad_reservations sklad_reservations[] @relation("SkladReservedBy")
|
||||||
roles roles? @relation(fields: [role_id], references: [id], onUpdate: NoAction, map: "users_ibfk_1")
|
roles roles? @relation(fields: [role_id], references: [id], onUpdate: NoAction, map: "users_ibfk_1")
|
||||||
|
|
||||||
@@index([is_active], map: "idx_users_is_active")
|
@@index([is_active], map: "idx_users_is_active")
|
||||||
@@ -647,3 +648,272 @@ enum attendance_leave_type {
|
|||||||
holiday
|
holiday
|
||||||
unpaid
|
unpaid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum sklad_receipt_status {
|
||||||
|
DRAFT
|
||||||
|
CONFIRMED
|
||||||
|
CANCELLED
|
||||||
|
}
|
||||||
|
|
||||||
|
enum sklad_issue_status {
|
||||||
|
DRAFT
|
||||||
|
CONFIRMED
|
||||||
|
CANCELLED
|
||||||
|
}
|
||||||
|
|
||||||
|
enum sklad_reservation_status {
|
||||||
|
ACTIVE
|
||||||
|
FULFILLED
|
||||||
|
CANCELLED
|
||||||
|
}
|
||||||
|
|
||||||
|
enum sklad_inventory_status {
|
||||||
|
DRAFT
|
||||||
|
CONFIRMED
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_categories {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String @db.VarChar(100)
|
||||||
|
description String? @db.Text
|
||||||
|
sort_order Int @default(0)
|
||||||
|
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||||
|
modified_at DateTime? @db.DateTime(0)
|
||||||
|
|
||||||
|
items sklad_items[]
|
||||||
|
|
||||||
|
@@map("sklad_categories")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_suppliers {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String @db.VarChar(255)
|
||||||
|
ico String? @db.VarChar(20)
|
||||||
|
dic String? @db.VarChar(20)
|
||||||
|
contact_person String? @db.VarChar(255)
|
||||||
|
email String? @db.VarChar(255)
|
||||||
|
phone String? @db.VarChar(50)
|
||||||
|
address String? @db.Text
|
||||||
|
notes String? @db.Text
|
||||||
|
is_active Boolean @default(true)
|
||||||
|
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||||
|
modified_at DateTime? @db.DateTime(0)
|
||||||
|
|
||||||
|
receipts sklad_receipts[]
|
||||||
|
|
||||||
|
@@map("sklad_suppliers")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_locations {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
code String @unique @db.VarChar(20)
|
||||||
|
name String @db.VarChar(100)
|
||||||
|
description String? @db.Text
|
||||||
|
is_active Boolean @default(true)
|
||||||
|
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||||
|
modified_at DateTime? @db.DateTime(0)
|
||||||
|
|
||||||
|
items sklad_item_locations[]
|
||||||
|
receipt_lines sklad_receipt_lines[]
|
||||||
|
issue_lines sklad_issue_lines[]
|
||||||
|
inventory_lines sklad_inventory_lines[]
|
||||||
|
|
||||||
|
@@map("sklad_locations")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_items {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
item_number String? @unique @db.VarChar(50)
|
||||||
|
name String @db.VarChar(255)
|
||||||
|
description String? @db.Text
|
||||||
|
category_id Int?
|
||||||
|
unit String @db.VarChar(20)
|
||||||
|
min_quantity Decimal? @db.Decimal(12, 3)
|
||||||
|
is_active Boolean @default(true)
|
||||||
|
notes String? @db.Text
|
||||||
|
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||||
|
modified_at DateTime? @db.DateTime(0)
|
||||||
|
|
||||||
|
category sklad_categories? @relation(fields: [category_id], references: [id])
|
||||||
|
batches sklad_batches[]
|
||||||
|
item_locations sklad_item_locations[]
|
||||||
|
receipt_lines sklad_receipt_lines[]
|
||||||
|
issue_lines sklad_issue_lines[]
|
||||||
|
reservations sklad_reservations[]
|
||||||
|
inventory_lines sklad_inventory_lines[]
|
||||||
|
|
||||||
|
@@index([category_id], map: "sklad_items_category_id")
|
||||||
|
@@map("sklad_items")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_batches {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
item_id Int
|
||||||
|
receipt_line_id Int @unique
|
||||||
|
quantity Decimal @db.Decimal(12, 3)
|
||||||
|
original_qty Decimal @db.Decimal(12, 3)
|
||||||
|
unit_price Decimal @db.Decimal(12, 2)
|
||||||
|
received_at DateTime? @db.DateTime(0)
|
||||||
|
is_consumed Boolean @default(false)
|
||||||
|
|
||||||
|
item sklad_items @relation(fields: [item_id], references: [id])
|
||||||
|
receipt_line sklad_receipt_lines @relation(fields: [receipt_line_id], references: [id])
|
||||||
|
issue_lines sklad_issue_lines[]
|
||||||
|
|
||||||
|
@@index([item_id, is_consumed, received_at], map: "sklad_batches_fifo")
|
||||||
|
@@map("sklad_batches")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_item_locations {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
item_id Int
|
||||||
|
location_id Int
|
||||||
|
quantity Decimal @default(0) @db.Decimal(12, 3)
|
||||||
|
|
||||||
|
item sklad_items @relation(fields: [item_id], references: [id])
|
||||||
|
location sklad_locations @relation(fields: [location_id], references: [id])
|
||||||
|
|
||||||
|
@@unique([item_id, location_id], map: "sklad_item_locations_unique")
|
||||||
|
@@map("sklad_item_locations")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_receipts {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
receipt_number String? @db.VarChar(50)
|
||||||
|
supplier_id Int?
|
||||||
|
delivery_note_number String? @db.VarChar(100)
|
||||||
|
delivery_note_date DateTime? @db.DateTime(0)
|
||||||
|
received_by Int?
|
||||||
|
notes String? @db.Text
|
||||||
|
status sklad_receipt_status @default(DRAFT)
|
||||||
|
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||||
|
modified_at DateTime? @db.DateTime(0)
|
||||||
|
|
||||||
|
supplier sklad_suppliers? @relation(fields: [supplier_id], references: [id])
|
||||||
|
received_by_user users? @relation("SkladReceivedBy", fields: [received_by], references: [id])
|
||||||
|
items sklad_receipt_lines[]
|
||||||
|
attachments sklad_receipt_attachments[]
|
||||||
|
|
||||||
|
@@map("sklad_receipts")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_receipt_lines {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
receipt_id Int
|
||||||
|
item_id Int
|
||||||
|
quantity Decimal @db.Decimal(12, 3)
|
||||||
|
unit_price Decimal @db.Decimal(12, 2)
|
||||||
|
location_id Int?
|
||||||
|
notes String? @db.VarChar(255)
|
||||||
|
|
||||||
|
receipt sklad_receipts @relation(fields: [receipt_id], references: [id])
|
||||||
|
item sklad_items @relation(fields: [item_id], references: [id])
|
||||||
|
location sklad_locations? @relation(fields: [location_id], references: [id])
|
||||||
|
batch sklad_batches?
|
||||||
|
|
||||||
|
@@index([receipt_id], map: "sklad_receipt_lines_receipt_id")
|
||||||
|
@@map("sklad_receipt_lines")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_receipt_attachments {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
receipt_id Int
|
||||||
|
file_name String @db.VarChar(255)
|
||||||
|
file_mime String @db.VarChar(100)
|
||||||
|
file_size Int
|
||||||
|
file_path String @db.VarChar(500)
|
||||||
|
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||||
|
|
||||||
|
receipt sklad_receipts @relation(fields: [receipt_id], references: [id])
|
||||||
|
|
||||||
|
@@map("sklad_receipt_attachments")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_issues {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
issue_number String? @db.VarChar(50)
|
||||||
|
project_id Int
|
||||||
|
issued_by Int?
|
||||||
|
notes String? @db.Text
|
||||||
|
status sklad_issue_status @default(DRAFT)
|
||||||
|
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||||
|
modified_at DateTime? @db.DateTime(0)
|
||||||
|
|
||||||
|
project projects @relation(fields: [project_id], references: [id])
|
||||||
|
issued_by_user users? @relation("SkladIssuedBy", fields: [issued_by], references: [id])
|
||||||
|
items sklad_issue_lines[]
|
||||||
|
|
||||||
|
@@map("sklad_issues")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_issue_lines {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
issue_id Int
|
||||||
|
item_id Int
|
||||||
|
batch_id Int
|
||||||
|
quantity Decimal @db.Decimal(12, 3)
|
||||||
|
location_id Int?
|
||||||
|
reservation_id Int?
|
||||||
|
notes String? @db.VarChar(255)
|
||||||
|
|
||||||
|
issue sklad_issues @relation(fields: [issue_id], references: [id])
|
||||||
|
item sklad_items @relation(fields: [item_id], references: [id])
|
||||||
|
batch sklad_batches @relation(fields: [batch_id], references: [id])
|
||||||
|
location sklad_locations? @relation(fields: [location_id], references: [id])
|
||||||
|
reservation sklad_reservations? @relation(fields: [reservation_id], references: [id])
|
||||||
|
|
||||||
|
@@index([issue_id], map: "sklad_issue_lines_issue_id")
|
||||||
|
@@map("sklad_issue_lines")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_reservations {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
item_id Int
|
||||||
|
project_id Int
|
||||||
|
quantity Decimal @db.Decimal(12, 3)
|
||||||
|
remaining_qty Decimal @db.Decimal(12, 3)
|
||||||
|
reserved_by Int?
|
||||||
|
notes String? @db.Text
|
||||||
|
status sklad_reservation_status @default(ACTIVE)
|
||||||
|
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||||
|
modified_at DateTime? @db.DateTime(0)
|
||||||
|
|
||||||
|
item sklad_items @relation(fields: [item_id], references: [id])
|
||||||
|
project projects @relation(fields: [project_id], references: [id])
|
||||||
|
reserved_by_user users? @relation("SkladReservedBy", fields: [reserved_by], references: [id])
|
||||||
|
issue_lines sklad_issue_lines[]
|
||||||
|
|
||||||
|
@@index([item_id, status], map: "sklad_reservations_item_status")
|
||||||
|
@@map("sklad_reservations")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_inventory_sessions {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
session_number String? @db.VarChar(50)
|
||||||
|
notes String? @db.Text
|
||||||
|
status sklad_inventory_status @default(DRAFT)
|
||||||
|
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||||
|
modified_at DateTime? @db.DateTime(0)
|
||||||
|
|
||||||
|
items sklad_inventory_lines[]
|
||||||
|
|
||||||
|
@@map("sklad_inventory_sessions")
|
||||||
|
}
|
||||||
|
|
||||||
|
model sklad_inventory_lines {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
session_id Int
|
||||||
|
item_id Int
|
||||||
|
location_id Int?
|
||||||
|
system_qty Decimal @db.Decimal(12, 3)
|
||||||
|
actual_qty Decimal @db.Decimal(12, 3)
|
||||||
|
difference Decimal @db.Decimal(12, 3)
|
||||||
|
notes String? @db.VarChar(255)
|
||||||
|
|
||||||
|
session sklad_inventory_sessions @relation(fields: [session_id], references: [id])
|
||||||
|
item sklad_items @relation(fields: [item_id], references: [id])
|
||||||
|
location sklad_locations? @relation(fields: [location_id], references: [id])
|
||||||
|
|
||||||
|
@@index([session_id], map: "sklad_inventory_lines_session_id")
|
||||||
|
@@map("sklad_inventory_lines")
|
||||||
|
}
|
||||||
|
|||||||
@@ -248,7 +248,13 @@ const PERMISSIONS: {
|
|||||||
name: "settings.company",
|
name: "settings.company",
|
||||||
display_name: "Nastavení společnosti",
|
display_name: "Nastavení společnosti",
|
||||||
module: "settings",
|
module: "settings",
|
||||||
description: "Upravovat údaje o společnosti, logo, 2FA",
|
description: "Upravovat údaje o společnosti, logo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "settings.system",
|
||||||
|
display_name: "Systémová nastavení",
|
||||||
|
module: "settings",
|
||||||
|
description: "Spravovat systémová nastavení, 2FA, e-maily, limity",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "settings.banking",
|
name: "settings.banking",
|
||||||
@@ -274,6 +280,32 @@ const PERMISSIONS: {
|
|||||||
module: "settings",
|
module: "settings",
|
||||||
description: "Prohlížet auditní logy",
|
description: "Prohlížet auditní logy",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Sklad
|
||||||
|
{
|
||||||
|
name: "warehouse.view",
|
||||||
|
display_name: "Zobrazit sklad",
|
||||||
|
module: "warehouse",
|
||||||
|
description: "Prohlížet stav skladu, položky, reporty a historii pohybů",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "warehouse.operate",
|
||||||
|
display_name: "Příjem a výdej",
|
||||||
|
module: "warehouse",
|
||||||
|
description: "Vytvářet a potvrzovat příjmy, výdeje a rezervace",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "warehouse.manage",
|
||||||
|
display_name: "Správa skladu",
|
||||||
|
module: "warehouse",
|
||||||
|
description: "Spravovat katalog materiálů, dodavatele, lokace a kategorie",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "warehouse.inventory",
|
||||||
|
display_name: "Inventura",
|
||||||
|
module: "warehouse",
|
||||||
|
description: "Vytvářet a potvrzovat inventurní sčítkání",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
@@ -401,7 +433,14 @@ async function main() {
|
|||||||
|
|
||||||
// 6. Initialize number_sequences for current year
|
// 6. Initialize number_sequences for current year
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
const types = ["quotation", "order", "invoice"];
|
const types = [
|
||||||
|
"quotation",
|
||||||
|
"order",
|
||||||
|
"invoice",
|
||||||
|
"warehouse_receipt",
|
||||||
|
"warehouse_issue",
|
||||||
|
"warehouse_inventory",
|
||||||
|
];
|
||||||
for (const type of types) {
|
for (const type of types) {
|
||||||
const existing = await prisma.number_sequences.findFirst({
|
const existing = await prisma.number_sequences.findFirst({
|
||||||
where: { type, year },
|
where: { type, year },
|
||||||
|
|||||||
1244
src/__tests__/warehouse.test.ts
Normal file
1244
src/__tests__/warehouse.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,6 @@ import "./layout.css";
|
|||||||
import "./components.css";
|
import "./components.css";
|
||||||
import "./tables.css";
|
import "./tables.css";
|
||||||
import "./datepicker.css";
|
import "./datepicker.css";
|
||||||
import "./bones/registry";
|
|
||||||
import "./filemanager.css";
|
import "./filemanager.css";
|
||||||
import "./pagination.css";
|
import "./pagination.css";
|
||||||
import "./responsive.css";
|
import "./responsive.css";
|
||||||
@@ -27,6 +26,7 @@ import "./attendance.css";
|
|||||||
import "./settings.css";
|
import "./settings.css";
|
||||||
import "./offers.css";
|
import "./offers.css";
|
||||||
import "./invoices.css";
|
import "./invoices.css";
|
||||||
|
import "./warehouse.css";
|
||||||
|
|
||||||
const Users = lazy(() => import("./pages/Users"));
|
const Users = lazy(() => import("./pages/Users"));
|
||||||
const Attendance = lazy(() => import("./pages/Attendance"));
|
const Attendance = lazy(() => import("./pages/Attendance"));
|
||||||
@@ -54,6 +54,31 @@ const InvoiceDetail = lazy(() => import("./pages/InvoiceDetail"));
|
|||||||
const Settings = lazy(() => import("./pages/Settings"));
|
const Settings = lazy(() => import("./pages/Settings"));
|
||||||
const AuditLog = lazy(() => import("./pages/AuditLog"));
|
const AuditLog = lazy(() => import("./pages/AuditLog"));
|
||||||
const NotFound = lazy(() => import("./pages/NotFound"));
|
const NotFound = lazy(() => import("./pages/NotFound"));
|
||||||
|
const Warehouse = lazy(() => import("./pages/Warehouse"));
|
||||||
|
const WarehouseItems = lazy(() => import("./pages/WarehouseItems"));
|
||||||
|
const WarehouseItemDetail = lazy(() => import("./pages/WarehouseItemDetail"));
|
||||||
|
const WarehouseReceipts = lazy(() => import("./pages/WarehouseReceipts"));
|
||||||
|
const WarehouseReceiptDetail = lazy(
|
||||||
|
() => import("./pages/WarehouseReceiptDetail"),
|
||||||
|
);
|
||||||
|
const WarehouseReceiptForm = lazy(() => import("./pages/WarehouseReceiptForm"));
|
||||||
|
const WarehouseIssues = lazy(() => import("./pages/WarehouseIssues"));
|
||||||
|
const WarehouseIssueDetail = lazy(() => import("./pages/WarehouseIssueDetail"));
|
||||||
|
const WarehouseIssueForm = lazy(() => import("./pages/WarehouseIssueForm"));
|
||||||
|
const WarehouseReservations = lazy(
|
||||||
|
() => import("./pages/WarehouseReservations"),
|
||||||
|
);
|
||||||
|
const WarehouseInventory = lazy(() => import("./pages/WarehouseInventory"));
|
||||||
|
const WarehouseInventoryDetail = lazy(
|
||||||
|
() => import("./pages/WarehouseInventoryDetail"),
|
||||||
|
);
|
||||||
|
const WarehouseInventoryForm = lazy(
|
||||||
|
() => import("./pages/WarehouseInventoryForm"),
|
||||||
|
);
|
||||||
|
const WarehouseReports = lazy(() => import("./pages/WarehouseReports"));
|
||||||
|
const WarehouseSuppliers = lazy(() => import("./pages/WarehouseSuppliers"));
|
||||||
|
const WarehouseLocations = lazy(() => import("./pages/WarehouseLocations"));
|
||||||
|
const WarehouseCategories = lazy(() => import("./pages/WarehouseCategories"));
|
||||||
|
|
||||||
export default function AdminApp() {
|
export default function AdminApp() {
|
||||||
return (
|
return (
|
||||||
@@ -127,6 +152,76 @@ export default function AdminApp() {
|
|||||||
<Route path="invoices/:id" element={<InvoiceDetail />} />
|
<Route path="invoices/:id" element={<InvoiceDetail />} />
|
||||||
<Route path="settings" element={<Settings />} />
|
<Route path="settings" element={<Settings />} />
|
||||||
<Route path="audit-log" element={<AuditLog />} />
|
<Route path="audit-log" element={<AuditLog />} />
|
||||||
|
<Route path="warehouse" element={<Warehouse />} />
|
||||||
|
<Route path="warehouse/items" element={<WarehouseItems />} />
|
||||||
|
<Route
|
||||||
|
path="warehouse/items/:id"
|
||||||
|
element={<WarehouseItemDetail />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/receipts"
|
||||||
|
element={<WarehouseReceipts />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/receipts/new"
|
||||||
|
element={<WarehouseReceiptForm />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/receipts/:id"
|
||||||
|
element={<WarehouseReceiptDetail />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/receipts/:id/edit"
|
||||||
|
element={<WarehouseReceiptForm />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/issues"
|
||||||
|
element={<WarehouseIssues />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/issues/new"
|
||||||
|
element={<WarehouseIssueForm />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/issues/:id"
|
||||||
|
element={<WarehouseIssueDetail />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/issues/:id/edit"
|
||||||
|
element={<WarehouseIssueForm />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/reservations"
|
||||||
|
element={<WarehouseReservations />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/inventory"
|
||||||
|
element={<WarehouseInventory />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/inventory/new"
|
||||||
|
element={<WarehouseInventoryForm />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/inventory/:id"
|
||||||
|
element={<WarehouseInventoryDetail />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/reports"
|
||||||
|
element={<WarehouseReports />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/suppliers"
|
||||||
|
element={<WarehouseSuppliers />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/locations"
|
||||||
|
element={<WarehouseLocations />}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="warehouse/categories"
|
||||||
|
element={<WarehouseCategories />}
|
||||||
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
@@ -318,12 +318,14 @@
|
|||||||
|
|
||||||
/* Leave Type Badges */
|
/* Leave Type Badges */
|
||||||
.attendance-leave-badge {
|
.attendance-leave-badge {
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
padding: 0.25rem 0.5rem;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.25rem 0.55rem;
|
||||||
border-radius: var(--border-radius-sm);
|
border-radius: var(--border-radius-sm);
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
margin-right: 0.25rem;
|
line-height: 1.2;
|
||||||
background: var(--bg-tertiary);
|
background: var(--bg-tertiary);
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,263 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "attendance-create",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 403,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
42,
|
|
||||||
100,
|
|
||||||
361,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
55,
|
|
||||||
92.5926,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
83,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
127,
|
|
||||||
92.5926,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
156,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
200,
|
|
||||||
92.5926,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
229,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
273,
|
|
||||||
92.5926,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
302,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
346,
|
|
||||||
19.2352,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "attendance-create",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 420,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
23.3738,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
46,
|
|
||||||
81.5217,
|
|
||||||
373,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
65,
|
|
||||||
76.3587,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
94,
|
|
||||||
76.3587,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
138,
|
|
||||||
76.3587,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
167,
|
|
||||||
76.3587,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
211,
|
|
||||||
76.3587,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
240,
|
|
||||||
76.3587,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
284,
|
|
||||||
76.3587,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
313,
|
|
||||||
76.3587,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
357,
|
|
||||||
9.1733,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "attendance-create",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 369,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
17.2722,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
46,
|
|
||||||
60.241,
|
|
||||||
323,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
65,
|
|
||||||
56.4257,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
93,
|
|
||||||
56.4257,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
129,
|
|
||||||
56.4257,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
156,
|
|
||||||
56.4257,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
192,
|
|
||||||
56.4257,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
219,
|
|
||||||
56.4257,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
255,
|
|
||||||
56.4257,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
282,
|
|
||||||
56.4257,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
318,
|
|
||||||
6.3771,
|
|
||||||
32,
|
|
||||||
8
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "7c4e446bf97f164a0fba87e2dd7df7d1"
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,506 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "dash-sessions",
|
|
||||||
"viewportWidth": 341,
|
|
||||||
"width": 341,
|
|
||||||
"height": 304,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
304,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.8123,
|
|
||||||
17,
|
|
||||||
34.9386,
|
|
||||||
17,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.3383,
|
|
||||||
13,
|
|
||||||
36.8493,
|
|
||||||
37,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0.2933,
|
|
||||||
63,
|
|
||||||
99.4135,
|
|
||||||
83,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.9853,
|
|
||||||
86,
|
|
||||||
10.5572,
|
|
||||||
36,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
7.6246,
|
|
||||||
95,
|
|
||||||
5.2786,
|
|
||||||
18,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
63.0407,
|
|
||||||
79,
|
|
||||||
19.5152,
|
|
||||||
27,
|
|
||||||
9999
|
|
||||||
],
|
|
||||||
[
|
|
||||||
19.0616,
|
|
||||||
110,
|
|
||||||
21.6734,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
43.081,
|
|
||||||
110,
|
|
||||||
1.0585,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
46.4855,
|
|
||||||
110,
|
|
||||||
26.8649,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.9853,
|
|
||||||
167,
|
|
||||||
10.5572,
|
|
||||||
36,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
7.6246,
|
|
||||||
176,
|
|
||||||
5.2786,
|
|
||||||
18,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
19.0616,
|
|
||||||
162,
|
|
||||||
75.9531,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
19.0616,
|
|
||||||
189,
|
|
||||||
21.6734,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
43.081,
|
|
||||||
189,
|
|
||||||
1.0585,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
46.4855,
|
|
||||||
189,
|
|
||||||
26.8649,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.9853,
|
|
||||||
246,
|
|
||||||
10.5572,
|
|
||||||
36,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
7.6246,
|
|
||||||
255,
|
|
||||||
5.2786,
|
|
||||||
18,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
19.0616,
|
|
||||||
241,
|
|
||||||
75.9531,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
19.0616,
|
|
||||||
267,
|
|
||||||
21.6734,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
43.081,
|
|
||||||
267,
|
|
||||||
1.0585,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
46.4855,
|
|
||||||
267,
|
|
||||||
26.8649,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "dash-sessions",
|
|
||||||
"viewportWidth": 726,
|
|
||||||
"width": 726,
|
|
||||||
"height": 319,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
319,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.6171,
|
|
||||||
19,
|
|
||||||
16.4106,
|
|
||||||
17,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
80.0749,
|
|
||||||
15,
|
|
||||||
17.308,
|
|
||||||
37,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0.1377,
|
|
||||||
67,
|
|
||||||
99.7245,
|
|
||||||
85,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.4435,
|
|
||||||
89,
|
|
||||||
5.5096,
|
|
||||||
40,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.9587,
|
|
||||||
100,
|
|
||||||
2.4793,
|
|
||||||
18,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
34.5278,
|
|
||||||
83,
|
|
||||||
9.1662,
|
|
||||||
27,
|
|
||||||
9999
|
|
||||||
],
|
|
||||||
[
|
|
||||||
11.157,
|
|
||||||
114,
|
|
||||||
11.0279,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.2868,
|
|
||||||
114,
|
|
||||||
0.5381,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.9268,
|
|
||||||
114,
|
|
||||||
13.6686,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.4435,
|
|
||||||
173,
|
|
||||||
5.5096,
|
|
||||||
40,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.9587,
|
|
||||||
184,
|
|
||||||
2.4793,
|
|
||||||
18,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
11.157,
|
|
||||||
168,
|
|
||||||
85.3994,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
11.157,
|
|
||||||
198,
|
|
||||||
11.0279,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.2868,
|
|
||||||
198,
|
|
||||||
0.5381,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.9268,
|
|
||||||
198,
|
|
||||||
13.6686,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.4435,
|
|
||||||
257,
|
|
||||||
5.5096,
|
|
||||||
40,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.9587,
|
|
||||||
268,
|
|
||||||
2.4793,
|
|
||||||
18,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
11.157,
|
|
||||||
251,
|
|
||||||
85.3994,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
11.157,
|
|
||||||
281,
|
|
||||||
11.0279,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.2868,
|
|
||||||
281,
|
|
||||||
0.5381,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.9268,
|
|
||||||
281,
|
|
||||||
13.6686,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "dash-sessions",
|
|
||||||
"viewportWidth": 484,
|
|
||||||
"width": 484,
|
|
||||||
"height": 309,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
309,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.9256,
|
|
||||||
15,
|
|
||||||
24.6158,
|
|
||||||
17,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.1785,
|
|
||||||
15,
|
|
||||||
23.8959,
|
|
||||||
29,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0.2066,
|
|
||||||
59,
|
|
||||||
99.5868,
|
|
||||||
83,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
5.1653,
|
|
||||||
80,
|
|
||||||
8.2645,
|
|
||||||
40,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
7.438,
|
|
||||||
91,
|
|
||||||
3.719,
|
|
||||||
18,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.7917,
|
|
||||||
76,
|
|
||||||
12.9358,
|
|
||||||
24,
|
|
||||||
9999
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.7355,
|
|
||||||
105,
|
|
||||||
16.5418,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
34.9303,
|
|
||||||
105,
|
|
||||||
0.8071,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
37.3902,
|
|
||||||
105,
|
|
||||||
20.503,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
5.1653,
|
|
||||||
164,
|
|
||||||
8.2645,
|
|
||||||
40,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
7.438,
|
|
||||||
175,
|
|
||||||
3.719,
|
|
||||||
18,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.7355,
|
|
||||||
158,
|
|
||||||
78.0992,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.7355,
|
|
||||||
188,
|
|
||||||
16.5418,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
34.9303,
|
|
||||||
188,
|
|
||||||
0.8071,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
37.3902,
|
|
||||||
188,
|
|
||||||
20.503,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
5.1653,
|
|
||||||
247,
|
|
||||||
8.2645,
|
|
||||||
40,
|
|
||||||
8,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
7.438,
|
|
||||||
258,
|
|
||||||
3.719,
|
|
||||||
18,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.7355,
|
|
||||||
242,
|
|
||||||
78.0992,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.7355,
|
|
||||||
271,
|
|
||||||
16.5418,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
34.9303,
|
|
||||||
271,
|
|
||||||
0.8071,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
37.3902,
|
|
||||||
271,
|
|
||||||
20.503,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "e057ca7b36a30c5971a4225ec3ad4680"
|
|
||||||
}
|
|
||||||
@@ -1,707 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "invoice-detail",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 466,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
3.4188,
|
|
||||||
12,
|
|
||||||
5.698,
|
|
||||||
20,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
14.8148,
|
|
||||||
9,
|
|
||||||
30.0881,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
52,
|
|
||||||
30.6713,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
34.0901,
|
|
||||||
52,
|
|
||||||
31.2455,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
68.7545,
|
|
||||||
52,
|
|
||||||
31.2455,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
112,
|
|
||||||
100,
|
|
||||||
152,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
125,
|
|
||||||
44.0171,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
154,
|
|
||||||
44.0171,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.2792,
|
|
||||||
125,
|
|
||||||
44.0171,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.2792,
|
|
||||||
154,
|
|
||||||
44.0171,
|
|
||||||
27,
|
|
||||||
9999
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
197,
|
|
||||||
44.0171,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
226,
|
|
||||||
44.0171,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.2792,
|
|
||||||
197,
|
|
||||||
44.0171,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.2792,
|
|
||||||
226,
|
|
||||||
44.0171,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
280,
|
|
||||||
100,
|
|
||||||
186,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
293,
|
|
||||||
92.5926,
|
|
||||||
17,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
322,
|
|
||||||
33.3066,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
37.0103,
|
|
||||||
322,
|
|
||||||
35.1718,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.1822,
|
|
||||||
322,
|
|
||||||
36.9836,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.1658,
|
|
||||||
322,
|
|
||||||
36.9881,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
353,
|
|
||||||
33.3066,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
37.0103,
|
|
||||||
353,
|
|
||||||
35.1718,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.1822,
|
|
||||||
353,
|
|
||||||
36.9836,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.1658,
|
|
||||||
353,
|
|
||||||
36.9881,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
387,
|
|
||||||
33.3066,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
37.0103,
|
|
||||||
387,
|
|
||||||
35.1718,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.1822,
|
|
||||||
387,
|
|
||||||
36.9836,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.1658,
|
|
||||||
387,
|
|
||||||
36.9881,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
420,
|
|
||||||
33.3066,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
37.0103,
|
|
||||||
420,
|
|
||||||
35.1718,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.1822,
|
|
||||||
420,
|
|
||||||
36.9836,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.1658,
|
|
||||||
420,
|
|
||||||
36.9881,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "invoice-detail",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 444,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
1.6304,
|
|
||||||
12,
|
|
||||||
2.7174,
|
|
||||||
20,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
7.0652,
|
|
||||||
7,
|
|
||||||
17.5378,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.9692,
|
|
||||||
0,
|
|
||||||
9.1733,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.7729,
|
|
||||||
0,
|
|
||||||
13.6379,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
89.0413,
|
|
||||||
0,
|
|
||||||
10.9587,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
60,
|
|
||||||
100,
|
|
||||||
164,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
79,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
108,
|
|
||||||
46.3315,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
79,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
108,
|
|
||||||
46.3315,
|
|
||||||
27,
|
|
||||||
9999
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
151,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
180,
|
|
||||||
46.3315,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
151,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
180,
|
|
||||||
46.3315,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
240,
|
|
||||||
100,
|
|
||||||
204,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
259,
|
|
||||||
94.837,
|
|
||||||
17,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
288,
|
|
||||||
22.3803,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.9618,
|
|
||||||
288,
|
|
||||||
23.5882,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.55,
|
|
||||||
288,
|
|
||||||
24.431,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.9811,
|
|
||||||
288,
|
|
||||||
24.4374,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
321,
|
|
||||||
22.3803,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.9618,
|
|
||||||
321,
|
|
||||||
23.5882,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.55,
|
|
||||||
321,
|
|
||||||
24.431,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.9811,
|
|
||||||
321,
|
|
||||||
24.4374,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
356,
|
|
||||||
22.3803,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.9618,
|
|
||||||
356,
|
|
||||||
23.5882,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.55,
|
|
||||||
356,
|
|
||||||
24.431,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.9811,
|
|
||||||
356,
|
|
||||||
24.4374,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
391,
|
|
||||||
22.3803,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.9618,
|
|
||||||
391,
|
|
||||||
23.5882,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.55,
|
|
||||||
391,
|
|
||||||
24.431,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.9811,
|
|
||||||
391,
|
|
||||||
24.4374,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "invoice-detail",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 457,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0.6024,
|
|
||||||
7,
|
|
||||||
2.008,
|
|
||||||
20,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.0161,
|
|
||||||
2,
|
|
||||||
12.9597,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.8407,
|
|
||||||
0,
|
|
||||||
6.3771,
|
|
||||||
34,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.4226,
|
|
||||||
0,
|
|
||||||
9.6762,
|
|
||||||
34,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
92.3036,
|
|
||||||
0,
|
|
||||||
7.6964,
|
|
||||||
34,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
50,
|
|
||||||
100,
|
|
||||||
160,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
69,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
96,
|
|
||||||
47.2892,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
69,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
96,
|
|
||||||
47.2892,
|
|
||||||
24,
|
|
||||||
9999
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
138,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
165,
|
|
||||||
47.2892,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
138,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
165,
|
|
||||||
47.2892,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
226,
|
|
||||||
100,
|
|
||||||
232,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
245,
|
|
||||||
96.1847,
|
|
||||||
17,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
273,
|
|
||||||
22.9606,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.8682,
|
|
||||||
273,
|
|
||||||
24.065,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.9332,
|
|
||||||
273,
|
|
||||||
24.578,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.5112,
|
|
||||||
273,
|
|
||||||
24.5811,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
311,
|
|
||||||
22.9606,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.8682,
|
|
||||||
311,
|
|
||||||
24.065,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.9332,
|
|
||||||
311,
|
|
||||||
24.578,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.5112,
|
|
||||||
311,
|
|
||||||
24.5811,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
354,
|
|
||||||
22.9606,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.8682,
|
|
||||||
354,
|
|
||||||
24.065,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.9332,
|
|
||||||
354,
|
|
||||||
24.578,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.5112,
|
|
||||||
354,
|
|
||||||
24.5811,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
396,
|
|
||||||
22.9606,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.8682,
|
|
||||||
396,
|
|
||||||
24.065,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.9332,
|
|
||||||
396,
|
|
||||||
24.578,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.5112,
|
|
||||||
396,
|
|
||||||
24.5811,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "934452d45a0bef9320dc379fb3f43bb5"
|
|
||||||
}
|
|
||||||
@@ -1,599 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "leave-approval",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 294,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
26,
|
|
||||||
100,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
61,
|
|
||||||
100,
|
|
||||||
233,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
74,
|
|
||||||
19.2753,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
22.979,
|
|
||||||
74,
|
|
||||||
18.7812,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.7601,
|
|
||||||
74,
|
|
||||||
23.0502,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.8104,
|
|
||||||
74,
|
|
||||||
23.0502,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.8606,
|
|
||||||
74,
|
|
||||||
9.4373,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
97.2979,
|
|
||||||
74,
|
|
||||||
54.4471,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
105,
|
|
||||||
19.2753,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
22.979,
|
|
||||||
105,
|
|
||||||
18.7812,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.7601,
|
|
||||||
105,
|
|
||||||
23.0502,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.8104,
|
|
||||||
105,
|
|
||||||
23.0502,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.8606,
|
|
||||||
105,
|
|
||||||
9.4373,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
97.2979,
|
|
||||||
105,
|
|
||||||
54.4471,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
159,
|
|
||||||
19.2753,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
22.979,
|
|
||||||
159,
|
|
||||||
18.7812,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.7601,
|
|
||||||
159,
|
|
||||||
23.0502,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.8104,
|
|
||||||
159,
|
|
||||||
23.0502,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.8606,
|
|
||||||
159,
|
|
||||||
9.4373,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
97.2979,
|
|
||||||
159,
|
|
||||||
54.4471,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
213,
|
|
||||||
19.2753,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
22.979,
|
|
||||||
213,
|
|
||||||
18.7812,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.7601,
|
|
||||||
213,
|
|
||||||
23.0502,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.8104,
|
|
||||||
213,
|
|
||||||
23.0502,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.8606,
|
|
||||||
213,
|
|
||||||
9.4373,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
97.2979,
|
|
||||||
213,
|
|
||||||
54.4471,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "leave-approval",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 299,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
29.4264,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
29.4264,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
232,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
86,
|
|
||||||
12.6911,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
15.2726,
|
|
||||||
86,
|
|
||||||
12.3769,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.6495,
|
|
||||||
86,
|
|
||||||
15.0921,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
42.7416,
|
|
||||||
86,
|
|
||||||
15.0921,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
57.8337,
|
|
||||||
86,
|
|
||||||
6.4856,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.3194,
|
|
||||||
86,
|
|
||||||
33.0991,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
119,
|
|
||||||
12.6911,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
15.2726,
|
|
||||||
119,
|
|
||||||
12.3769,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.6495,
|
|
||||||
119,
|
|
||||||
15.0921,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
42.7416,
|
|
||||||
119,
|
|
||||||
15.0921,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
57.8337,
|
|
||||||
119,
|
|
||||||
6.4856,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.3194,
|
|
||||||
119,
|
|
||||||
33.0991,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
173,
|
|
||||||
12.6911,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
15.2726,
|
|
||||||
173,
|
|
||||||
12.3769,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.6495,
|
|
||||||
173,
|
|
||||||
15.0921,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
42.7416,
|
|
||||||
173,
|
|
||||||
15.0921,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
57.8337,
|
|
||||||
173,
|
|
||||||
6.4856,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.3194,
|
|
||||||
173,
|
|
||||||
33.0991,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
227,
|
|
||||||
12.6911,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
15.2726,
|
|
||||||
227,
|
|
||||||
12.3769,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.6495,
|
|
||||||
227,
|
|
||||||
15.0921,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
42.7416,
|
|
||||||
227,
|
|
||||||
15.0921,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
57.8337,
|
|
||||||
227,
|
|
||||||
6.4856,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.3194,
|
|
||||||
227,
|
|
||||||
33.0991,
|
|
||||||
54,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "leave-approval",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 299,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
21.7448,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
21.7448,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
232,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
86,
|
|
||||||
13.8664,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
15.774,
|
|
||||||
86,
|
|
||||||
13.5589,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
29.333,
|
|
||||||
86,
|
|
||||||
16.196,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.529,
|
|
||||||
86,
|
|
||||||
16.196,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
61.725,
|
|
||||||
86,
|
|
||||||
7.8878,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.6128,
|
|
||||||
86,
|
|
||||||
28.4795,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
124,
|
|
||||||
13.8664,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
15.774,
|
|
||||||
124,
|
|
||||||
13.5589,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
29.333,
|
|
||||||
124,
|
|
||||||
16.196,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.529,
|
|
||||||
124,
|
|
||||||
16.196,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
61.725,
|
|
||||||
124,
|
|
||||||
7.8878,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.6128,
|
|
||||||
124,
|
|
||||||
28.4795,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
176,
|
|
||||||
13.8664,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
15.774,
|
|
||||||
176,
|
|
||||||
13.5589,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
29.333,
|
|
||||||
176,
|
|
||||||
16.196,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.529,
|
|
||||||
176,
|
|
||||||
16.196,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
61.725,
|
|
||||||
176,
|
|
||||||
7.8878,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.6128,
|
|
||||||
176,
|
|
||||||
28.4795,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
228,
|
|
||||||
13.8664,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
15.774,
|
|
||||||
228,
|
|
||||||
13.5589,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
29.333,
|
|
||||||
228,
|
|
||||||
16.196,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.529,
|
|
||||||
228,
|
|
||||||
16.196,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
61.725,
|
|
||||||
228,
|
|
||||||
7.8878,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.6128,
|
|
||||||
228,
|
|
||||||
28.4795,
|
|
||||||
52,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "4b74917f659334073252a738cfa9c4ac"
|
|
||||||
}
|
|
||||||
@@ -1,704 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "leave-requests",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 367,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
26,
|
|
||||||
100,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
53,
|
|
||||||
100,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
113,
|
|
||||||
100,
|
|
||||||
254,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
126,
|
|
||||||
21.1806,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.8843,
|
|
||||||
126,
|
|
||||||
20.6375,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.5217,
|
|
||||||
126,
|
|
||||||
25.3294,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.8511,
|
|
||||||
126,
|
|
||||||
25.3294,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
96.1806,
|
|
||||||
126,
|
|
||||||
10.3677,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
106.5483,
|
|
||||||
126,
|
|
||||||
20.7977,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
127.346,
|
|
||||||
126,
|
|
||||||
18.8079,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
157,
|
|
||||||
21.1806,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.8843,
|
|
||||||
157,
|
|
||||||
20.6375,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.5217,
|
|
||||||
157,
|
|
||||||
25.3294,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.8511,
|
|
||||||
157,
|
|
||||||
25.3294,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
96.1806,
|
|
||||||
157,
|
|
||||||
10.3677,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
106.5483,
|
|
||||||
157,
|
|
||||||
20.7977,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
127.346,
|
|
||||||
157,
|
|
||||||
18.8079,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
218,
|
|
||||||
21.1806,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.8843,
|
|
||||||
218,
|
|
||||||
20.6375,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.5217,
|
|
||||||
218,
|
|
||||||
25.3294,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.8511,
|
|
||||||
218,
|
|
||||||
25.3294,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
96.1806,
|
|
||||||
218,
|
|
||||||
10.3677,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
106.5483,
|
|
||||||
218,
|
|
||||||
20.7977,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
127.346,
|
|
||||||
218,
|
|
||||||
18.8079,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
279,
|
|
||||||
21.1806,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
24.8843,
|
|
||||||
279,
|
|
||||||
20.6375,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.5217,
|
|
||||||
279,
|
|
||||||
25.3294,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.8511,
|
|
||||||
279,
|
|
||||||
25.3294,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
96.1806,
|
|
||||||
279,
|
|
||||||
10.3677,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
106.5483,
|
|
||||||
279,
|
|
||||||
20.7977,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
127.346,
|
|
||||||
279,
|
|
||||||
18.8079,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "leave-requests",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 320,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
27.1888,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
27.1888,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.6914,
|
|
||||||
4,
|
|
||||||
16.3086,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
253,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
86,
|
|
||||||
14.313,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.8945,
|
|
||||||
86,
|
|
||||||
13.9585,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.853,
|
|
||||||
86,
|
|
||||||
17.0219,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.8749,
|
|
||||||
86,
|
|
||||||
17.0219,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.8968,
|
|
||||||
86,
|
|
||||||
7.3157,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.2126,
|
|
||||||
86,
|
|
||||||
13.2006,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4131,
|
|
||||||
86,
|
|
||||||
12.0053,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
119,
|
|
||||||
14.313,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.8945,
|
|
||||||
119,
|
|
||||||
13.9585,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.853,
|
|
||||||
119,
|
|
||||||
17.0219,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.8749,
|
|
||||||
119,
|
|
||||||
17.0219,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.8968,
|
|
||||||
119,
|
|
||||||
7.3157,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.2126,
|
|
||||||
119,
|
|
||||||
13.2006,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4131,
|
|
||||||
119,
|
|
||||||
12.0053,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
180,
|
|
||||||
14.313,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.8945,
|
|
||||||
180,
|
|
||||||
13.9585,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.853,
|
|
||||||
180,
|
|
||||||
17.0219,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.8749,
|
|
||||||
180,
|
|
||||||
17.0219,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.8968,
|
|
||||||
180,
|
|
||||||
7.3157,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.2126,
|
|
||||||
180,
|
|
||||||
13.2006,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4131,
|
|
||||||
180,
|
|
||||||
12.0053,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
241,
|
|
||||||
14.313,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.8945,
|
|
||||||
241,
|
|
||||||
13.9585,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.853,
|
|
||||||
241,
|
|
||||||
17.0219,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.8749,
|
|
||||||
241,
|
|
||||||
17.0219,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.8968,
|
|
||||||
241,
|
|
||||||
7.3157,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.2126,
|
|
||||||
241,
|
|
||||||
13.2006,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4131,
|
|
||||||
241,
|
|
||||||
12.0053,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "leave-requests",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 308,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
20.0913,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
20.0913,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
88.3503,
|
|
||||||
10,
|
|
||||||
11.6497,
|
|
||||||
32,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
241,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
86,
|
|
||||||
14.9787,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.8863,
|
|
||||||
86,
|
|
||||||
14.6461,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
31.5324,
|
|
||||||
86,
|
|
||||||
17.495,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.0274,
|
|
||||||
86,
|
|
||||||
17.495,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
66.5223,
|
|
||||||
86,
|
|
||||||
8.52,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
75.0424,
|
|
||||||
86,
|
|
||||||
12.74,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.7824,
|
|
||||||
86,
|
|
||||||
10.31,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
124,
|
|
||||||
14.9787,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.8863,
|
|
||||||
124,
|
|
||||||
14.6461,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
31.5324,
|
|
||||||
124,
|
|
||||||
17.495,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.0274,
|
|
||||||
124,
|
|
||||||
17.495,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
66.5223,
|
|
||||||
124,
|
|
||||||
8.52,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
75.0424,
|
|
||||||
124,
|
|
||||||
12.74,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.7824,
|
|
||||||
124,
|
|
||||||
10.31,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
179,
|
|
||||||
14.9787,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.8863,
|
|
||||||
179,
|
|
||||||
14.6461,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
31.5324,
|
|
||||||
179,
|
|
||||||
17.495,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.0274,
|
|
||||||
179,
|
|
||||||
17.495,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
66.5223,
|
|
||||||
179,
|
|
||||||
8.52,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
75.0424,
|
|
||||||
179,
|
|
||||||
12.74,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.7824,
|
|
||||||
179,
|
|
||||||
10.31,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
234,
|
|
||||||
14.9787,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
16.8863,
|
|
||||||
234,
|
|
||||||
14.6461,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
31.5324,
|
|
||||||
234,
|
|
||||||
17.495,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.0274,
|
|
||||||
234,
|
|
||||||
17.495,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
66.5223,
|
|
||||||
234,
|
|
||||||
8.52,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
75.0424,
|
|
||||||
234,
|
|
||||||
12.74,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.7824,
|
|
||||||
234,
|
|
||||||
10.31,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "125231cbf4c6abc4e73bb48732dc9353"
|
|
||||||
}
|
|
||||||
@@ -1,620 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "offer-detail",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 483,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
12.5356,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
52,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
86,
|
|
||||||
100,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
146,
|
|
||||||
100,
|
|
||||||
338,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
159,
|
|
||||||
44.0171,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
187,
|
|
||||||
44.0171,
|
|
||||||
46,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.2792,
|
|
||||||
159,
|
|
||||||
44.0171,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.2792,
|
|
||||||
187,
|
|
||||||
44.0171,
|
|
||||||
46,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
249,
|
|
||||||
44.0171,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
278,
|
|
||||||
44.0171,
|
|
||||||
46,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.2792,
|
|
||||||
249,
|
|
||||||
44.0171,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.2792,
|
|
||||||
278,
|
|
||||||
44.0171,
|
|
||||||
46,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
339,
|
|
||||||
34.4062,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.1099,
|
|
||||||
339,
|
|
||||||
34.8157,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.9256,
|
|
||||||
339,
|
|
||||||
36.6097,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.5353,
|
|
||||||
339,
|
|
||||||
36.6186,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
370,
|
|
||||||
34.4062,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.1099,
|
|
||||||
370,
|
|
||||||
34.8157,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.9256,
|
|
||||||
370,
|
|
||||||
36.6097,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.5353,
|
|
||||||
370,
|
|
||||||
36.6186,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
404,
|
|
||||||
34.4062,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.1099,
|
|
||||||
404,
|
|
||||||
34.8157,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.9256,
|
|
||||||
404,
|
|
||||||
36.6097,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.5353,
|
|
||||||
404,
|
|
||||||
36.6186,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
437,
|
|
||||||
34.4062,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.1099,
|
|
||||||
437,
|
|
||||||
34.8157,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.9256,
|
|
||||||
437,
|
|
||||||
36.6097,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.5353,
|
|
||||||
437,
|
|
||||||
36.6186,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "offer-detail",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 416,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
5.9783,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.4829,
|
|
||||||
7,
|
|
||||||
19.8412,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
90.8267,
|
|
||||||
0,
|
|
||||||
9.1733,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
60,
|
|
||||||
100,
|
|
||||||
356,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
79,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
108,
|
|
||||||
46.3315,
|
|
||||||
46,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
79,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
108,
|
|
||||||
46.3315,
|
|
||||||
46,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
169,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
198,
|
|
||||||
46.3315,
|
|
||||||
46,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
169,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
198,
|
|
||||||
46.3315,
|
|
||||||
46,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
260,
|
|
||||||
22.8558,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.4373,
|
|
||||||
260,
|
|
||||||
23.4333,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.8706,
|
|
||||||
260,
|
|
||||||
24.2718,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.1424,
|
|
||||||
260,
|
|
||||||
24.2761,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
292,
|
|
||||||
22.8558,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.4373,
|
|
||||||
292,
|
|
||||||
23.4333,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.8706,
|
|
||||||
292,
|
|
||||||
24.2718,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.1424,
|
|
||||||
292,
|
|
||||||
24.2761,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
327,
|
|
||||||
22.8558,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.4373,
|
|
||||||
327,
|
|
||||||
23.4333,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.8706,
|
|
||||||
327,
|
|
||||||
24.2718,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.1424,
|
|
||||||
327,
|
|
||||||
24.2761,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
362,
|
|
||||||
22.8558,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.4373,
|
|
||||||
362,
|
|
||||||
23.4333,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.8706,
|
|
||||||
362,
|
|
||||||
24.2718,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.1424,
|
|
||||||
362,
|
|
||||||
24.2761,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "offer-detail",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 419,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
3.2129,
|
|
||||||
32,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.0878,
|
|
||||||
1,
|
|
||||||
14.6618,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
93.6229,
|
|
||||||
0,
|
|
||||||
6.3771,
|
|
||||||
32,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
48,
|
|
||||||
100,
|
|
||||||
371,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
67,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
94,
|
|
||||||
47.2892,
|
|
||||||
41,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
67,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
94,
|
|
||||||
47.2892,
|
|
||||||
41,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
151,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
178,
|
|
||||||
47.2892,
|
|
||||||
41,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
151,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
178,
|
|
||||||
47.2892,
|
|
||||||
41,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
235,
|
|
||||||
23.2194,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.1271,
|
|
||||||
235,
|
|
||||||
23.9787,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.1058,
|
|
||||||
235,
|
|
||||||
24.4917,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.5975,
|
|
||||||
235,
|
|
||||||
24.4949,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
273,
|
|
||||||
23.2194,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.1271,
|
|
||||||
273,
|
|
||||||
23.9787,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.1058,
|
|
||||||
273,
|
|
||||||
24.4917,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.5975,
|
|
||||||
273,
|
|
||||||
24.4949,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
316,
|
|
||||||
23.2194,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.1271,
|
|
||||||
316,
|
|
||||||
23.9787,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.1058,
|
|
||||||
316,
|
|
||||||
24.4917,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.5975,
|
|
||||||
316,
|
|
||||||
24.4949,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
358,
|
|
||||||
23.2194,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.1271,
|
|
||||||
358,
|
|
||||||
23.9787,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.1058,
|
|
||||||
358,
|
|
||||||
24.4917,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
73.5975,
|
|
||||||
358,
|
|
||||||
24.4949,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "67676fde7dd5c432922d819fc9bf48db"
|
|
||||||
}
|
|
||||||
@@ -1,641 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "offers-customers",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 549,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
26,
|
|
||||||
100,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
53,
|
|
||||||
100,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
113,
|
|
||||||
100,
|
|
||||||
436,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
126,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
186,
|
|
||||||
34.562,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.2657,
|
|
||||||
186,
|
|
||||||
23.7936,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.0593,
|
|
||||||
186,
|
|
||||||
32.4653,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
94.5246,
|
|
||||||
186,
|
|
||||||
51.6293,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
217,
|
|
||||||
34.562,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.2657,
|
|
||||||
217,
|
|
||||||
23.7936,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.0593,
|
|
||||||
217,
|
|
||||||
32.4653,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
94.5246,
|
|
||||||
217,
|
|
||||||
51.6293,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
278,
|
|
||||||
34.562,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.2657,
|
|
||||||
278,
|
|
||||||
23.7936,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.0593,
|
|
||||||
278,
|
|
||||||
32.4653,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
94.5246,
|
|
||||||
278,
|
|
||||||
51.6293,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
339,
|
|
||||||
34.562,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.2657,
|
|
||||||
339,
|
|
||||||
23.7936,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.0593,
|
|
||||||
339,
|
|
||||||
32.4653,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
94.5246,
|
|
||||||
339,
|
|
||||||
51.6293,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
400,
|
|
||||||
34.562,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.2657,
|
|
||||||
400,
|
|
||||||
23.7936,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.0593,
|
|
||||||
400,
|
|
||||||
32.4653,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
94.5246,
|
|
||||||
400,
|
|
||||||
51.6293,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
461,
|
|
||||||
34.562,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.2657,
|
|
||||||
461,
|
|
||||||
23.7936,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.0593,
|
|
||||||
461,
|
|
||||||
32.4653,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
94.5246,
|
|
||||||
461,
|
|
||||||
51.6293,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "offers-customers",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 502,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
12.9458,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
12.9458,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
80.6322,
|
|
||||||
4,
|
|
||||||
19.3678,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
435,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
86,
|
|
||||||
94.837,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
146,
|
|
||||||
23.2868,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.8683,
|
|
||||||
146,
|
|
||||||
16.453,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
42.3212,
|
|
||||||
146,
|
|
||||||
21.9175,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.2387,
|
|
||||||
146,
|
|
||||||
33.1798,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
179,
|
|
||||||
23.2868,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.8683,
|
|
||||||
179,
|
|
||||||
16.453,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
42.3212,
|
|
||||||
179,
|
|
||||||
21.9175,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.2387,
|
|
||||||
179,
|
|
||||||
33.1798,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
240,
|
|
||||||
23.2868,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.8683,
|
|
||||||
240,
|
|
||||||
16.453,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
42.3212,
|
|
||||||
240,
|
|
||||||
21.9175,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.2387,
|
|
||||||
240,
|
|
||||||
33.1798,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
301,
|
|
||||||
23.2868,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.8683,
|
|
||||||
301,
|
|
||||||
16.453,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
42.3212,
|
|
||||||
301,
|
|
||||||
21.9175,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.2387,
|
|
||||||
301,
|
|
||||||
33.1798,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
362,
|
|
||||||
23.2868,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.8683,
|
|
||||||
362,
|
|
||||||
16.453,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
42.3212,
|
|
||||||
362,
|
|
||||||
21.9175,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.2387,
|
|
||||||
362,
|
|
||||||
33.1798,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
423,
|
|
||||||
23.2868,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.8683,
|
|
||||||
423,
|
|
||||||
16.453,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
42.3212,
|
|
||||||
423,
|
|
||||||
21.9175,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
64.2387,
|
|
||||||
423,
|
|
||||||
33.1798,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "offers-customers",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 470,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
9.5664,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
9.5664,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
86.0897,
|
|
||||||
10,
|
|
||||||
13.9103,
|
|
||||||
32,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
403,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
86,
|
|
||||||
96.1847,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
138,
|
|
||||||
25.673,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.5806,
|
|
||||||
138,
|
|
||||||
19.0904,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
46.6711,
|
|
||||||
138,
|
|
||||||
24.3254,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.9965,
|
|
||||||
138,
|
|
||||||
27.0959,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
176,
|
|
||||||
25.673,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.5806,
|
|
||||||
176,
|
|
||||||
19.0904,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
46.6711,
|
|
||||||
176,
|
|
||||||
24.3254,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.9965,
|
|
||||||
176,
|
|
||||||
27.0959,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
231,
|
|
||||||
25.673,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.5806,
|
|
||||||
231,
|
|
||||||
19.0904,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
46.6711,
|
|
||||||
231,
|
|
||||||
24.3254,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.9965,
|
|
||||||
231,
|
|
||||||
27.0959,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
286,
|
|
||||||
25.673,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.5806,
|
|
||||||
286,
|
|
||||||
19.0904,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
46.6711,
|
|
||||||
286,
|
|
||||||
24.3254,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.9965,
|
|
||||||
286,
|
|
||||||
27.0959,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
341,
|
|
||||||
25.673,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.5806,
|
|
||||||
341,
|
|
||||||
19.0904,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
46.6711,
|
|
||||||
341,
|
|
||||||
24.3254,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.9965,
|
|
||||||
341,
|
|
||||||
27.0959,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
396,
|
|
||||||
25.673,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.5806,
|
|
||||||
396,
|
|
||||||
19.0904,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
46.6711,
|
|
||||||
396,
|
|
||||||
24.3254,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.9965,
|
|
||||||
396,
|
|
||||||
27.0959,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "63b2dec2b6ceb84d931a000ab8b669dd"
|
|
||||||
}
|
|
||||||
@@ -1,452 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "offers-templates",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 436,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
436,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
13,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
73,
|
|
||||||
39.659,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
43.3627,
|
|
||||||
73,
|
|
||||||
39.6857,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.0484,
|
|
||||||
73,
|
|
||||||
63.1054,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
104,
|
|
||||||
39.659,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
43.3627,
|
|
||||||
104,
|
|
||||||
39.6857,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.0484,
|
|
||||||
104,
|
|
||||||
63.1054,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
165,
|
|
||||||
39.659,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
43.3627,
|
|
||||||
165,
|
|
||||||
39.6857,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.0484,
|
|
||||||
165,
|
|
||||||
63.1054,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
226,
|
|
||||||
39.659,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
43.3627,
|
|
||||||
226,
|
|
||||||
39.6857,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.0484,
|
|
||||||
226,
|
|
||||||
63.1054,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
287,
|
|
||||||
39.659,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
43.3627,
|
|
||||||
287,
|
|
||||||
39.6857,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.0484,
|
|
||||||
287,
|
|
||||||
63.1054,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
348,
|
|
||||||
39.659,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
43.3627,
|
|
||||||
348,
|
|
||||||
39.6857,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.0484,
|
|
||||||
348,
|
|
||||||
63.1054,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "offers-templates",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 435,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
435,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
19,
|
|
||||||
94.837,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
79,
|
|
||||||
26.9744,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
29.5559,
|
|
||||||
79,
|
|
||||||
26.9977,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
56.5536,
|
|
||||||
79,
|
|
||||||
40.8649,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
112,
|
|
||||||
26.9744,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
29.5559,
|
|
||||||
112,
|
|
||||||
26.9977,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
56.5536,
|
|
||||||
112,
|
|
||||||
40.8649,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
173,
|
|
||||||
26.9744,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
29.5559,
|
|
||||||
173,
|
|
||||||
26.9977,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
56.5536,
|
|
||||||
173,
|
|
||||||
40.8649,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
234,
|
|
||||||
26.9744,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
29.5559,
|
|
||||||
234,
|
|
||||||
26.9977,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
56.5536,
|
|
||||||
234,
|
|
||||||
40.8649,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
295,
|
|
||||||
26.9744,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
29.5559,
|
|
||||||
295,
|
|
||||||
26.9977,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
56.5536,
|
|
||||||
295,
|
|
||||||
40.8649,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
356,
|
|
||||||
26.9744,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
29.5559,
|
|
||||||
356,
|
|
||||||
26.9977,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
56.5536,
|
|
||||||
356,
|
|
||||||
40.8649,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "offers-templates",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 403,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
403,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
19,
|
|
||||||
96.1847,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
71,
|
|
||||||
30.8719,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
32.7796,
|
|
||||||
71,
|
|
||||||
30.897,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
63.6766,
|
|
||||||
71,
|
|
||||||
34.4158,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
109,
|
|
||||||
30.8719,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
32.7796,
|
|
||||||
109,
|
|
||||||
30.897,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
63.6766,
|
|
||||||
109,
|
|
||||||
34.4158,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
164,
|
|
||||||
30.8719,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
32.7796,
|
|
||||||
164,
|
|
||||||
30.897,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
63.6766,
|
|
||||||
164,
|
|
||||||
34.4158,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
219,
|
|
||||||
30.8719,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
32.7796,
|
|
||||||
219,
|
|
||||||
30.897,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
63.6766,
|
|
||||||
219,
|
|
||||||
34.4158,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
274,
|
|
||||||
30.8719,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
32.7796,
|
|
||||||
274,
|
|
||||||
30.897,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
63.6766,
|
|
||||||
274,
|
|
||||||
34.4158,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
329,
|
|
||||||
30.8719,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
32.7796,
|
|
||||||
329,
|
|
||||||
30.897,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
63.6766,
|
|
||||||
329,
|
|
||||||
34.4158,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "5e5881859bd932a42345c69a6a30ca65"
|
|
||||||
}
|
|
||||||
@@ -1,872 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "offers",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 497,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
26,
|
|
||||||
100,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
61,
|
|
||||||
100,
|
|
||||||
436,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
74,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
134,
|
|
||||||
26.7495,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
134,
|
|
||||||
20.6019,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.055,
|
|
||||||
134,
|
|
||||||
21.4165,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.4715,
|
|
||||||
134,
|
|
||||||
23.0502,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
95.5217,
|
|
||||||
134,
|
|
||||||
23.0502,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
118.5719,
|
|
||||||
134,
|
|
||||||
30.7692,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
165,
|
|
||||||
26.7495,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
165,
|
|
||||||
20.6019,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.055,
|
|
||||||
165,
|
|
||||||
21.4165,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.4715,
|
|
||||||
165,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
95.5217,
|
|
||||||
165,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
118.5719,
|
|
||||||
165,
|
|
||||||
30.7692,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
226,
|
|
||||||
26.7495,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
226,
|
|
||||||
20.6019,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.055,
|
|
||||||
226,
|
|
||||||
21.4165,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.4715,
|
|
||||||
226,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
95.5217,
|
|
||||||
226,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
118.5719,
|
|
||||||
226,
|
|
||||||
30.7692,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
287,
|
|
||||||
26.7495,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
287,
|
|
||||||
20.6019,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.055,
|
|
||||||
287,
|
|
||||||
21.4165,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.4715,
|
|
||||||
287,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
95.5217,
|
|
||||||
287,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
118.5719,
|
|
||||||
287,
|
|
||||||
30.7692,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
348,
|
|
||||||
26.7495,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
348,
|
|
||||||
20.6019,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.055,
|
|
||||||
348,
|
|
||||||
21.4165,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.4715,
|
|
||||||
348,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
95.5217,
|
|
||||||
348,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
118.5719,
|
|
||||||
348,
|
|
||||||
30.7692,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
409,
|
|
||||||
26.7495,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
409,
|
|
||||||
20.6019,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.055,
|
|
||||||
409,
|
|
||||||
21.4165,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.4715,
|
|
||||||
409,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
95.5217,
|
|
||||||
409,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
118.5719,
|
|
||||||
409,
|
|
||||||
30.7692,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "offers",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 502,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
11.3451,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
11.3451,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
435,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
86,
|
|
||||||
94.837,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
146,
|
|
||||||
17.6779,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2594,
|
|
||||||
146,
|
|
||||||
13.7101,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
33.9695,
|
|
||||||
146,
|
|
||||||
13.3301,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2996,
|
|
||||||
146,
|
|
||||||
15.2917,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.5913,
|
|
||||||
146,
|
|
||||||
15.2917,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
77.883,
|
|
||||||
146,
|
|
||||||
19.5355,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
179,
|
|
||||||
17.6779,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2594,
|
|
||||||
179,
|
|
||||||
13.7101,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
33.9695,
|
|
||||||
179,
|
|
||||||
13.3301,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2996,
|
|
||||||
179,
|
|
||||||
15.2917,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.5913,
|
|
||||||
179,
|
|
||||||
15.2917,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
77.883,
|
|
||||||
179,
|
|
||||||
19.5355,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
240,
|
|
||||||
17.6779,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2594,
|
|
||||||
240,
|
|
||||||
13.7101,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
33.9695,
|
|
||||||
240,
|
|
||||||
13.3301,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2996,
|
|
||||||
240,
|
|
||||||
15.2917,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.5913,
|
|
||||||
240,
|
|
||||||
15.2917,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
77.883,
|
|
||||||
240,
|
|
||||||
19.5355,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
301,
|
|
||||||
17.6779,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2594,
|
|
||||||
301,
|
|
||||||
13.7101,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
33.9695,
|
|
||||||
301,
|
|
||||||
13.3301,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2996,
|
|
||||||
301,
|
|
||||||
15.2917,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.5913,
|
|
||||||
301,
|
|
||||||
15.2917,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
77.883,
|
|
||||||
301,
|
|
||||||
19.5355,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
362,
|
|
||||||
17.6779,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2594,
|
|
||||||
362,
|
|
||||||
13.7101,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
33.9695,
|
|
||||||
362,
|
|
||||||
13.3301,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2996,
|
|
||||||
362,
|
|
||||||
15.2917,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.5913,
|
|
||||||
362,
|
|
||||||
15.2917,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
77.883,
|
|
||||||
362,
|
|
||||||
19.5355,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
423,
|
|
||||||
17.6779,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2594,
|
|
||||||
423,
|
|
||||||
13.7101,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
33.9695,
|
|
||||||
423,
|
|
||||||
13.3301,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2996,
|
|
||||||
423,
|
|
||||||
15.2917,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.5913,
|
|
||||||
423,
|
|
||||||
15.2917,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
77.883,
|
|
||||||
423,
|
|
||||||
19.5355,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "offers",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 470,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
8.3835,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
8.3835,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
403,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
86,
|
|
||||||
96.1847,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
138,
|
|
||||||
18.8912,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.7988,
|
|
||||||
138,
|
|
||||||
15.0085,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.8073,
|
|
||||||
138,
|
|
||||||
13.333,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.1403,
|
|
||||||
138,
|
|
||||||
16.5553,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.6956,
|
|
||||||
138,
|
|
||||||
16.5553,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
82.2509,
|
|
||||||
138,
|
|
||||||
15.8415,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
176,
|
|
||||||
18.8912,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.7988,
|
|
||||||
176,
|
|
||||||
15.0085,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.8073,
|
|
||||||
176,
|
|
||||||
13.333,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.1403,
|
|
||||||
176,
|
|
||||||
16.5553,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.6956,
|
|
||||||
176,
|
|
||||||
16.5553,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
82.2509,
|
|
||||||
176,
|
|
||||||
15.8415,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
231,
|
|
||||||
18.8912,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.7988,
|
|
||||||
231,
|
|
||||||
15.0085,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.8073,
|
|
||||||
231,
|
|
||||||
13.333,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.1403,
|
|
||||||
231,
|
|
||||||
16.5553,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.6956,
|
|
||||||
231,
|
|
||||||
16.5553,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
82.2509,
|
|
||||||
231,
|
|
||||||
15.8415,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
286,
|
|
||||||
18.8912,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.7988,
|
|
||||||
286,
|
|
||||||
15.0085,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.8073,
|
|
||||||
286,
|
|
||||||
13.333,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.1403,
|
|
||||||
286,
|
|
||||||
16.5553,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.6956,
|
|
||||||
286,
|
|
||||||
16.5553,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
82.2509,
|
|
||||||
286,
|
|
||||||
15.8415,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
341,
|
|
||||||
18.8912,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.7988,
|
|
||||||
341,
|
|
||||||
15.0085,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.8073,
|
|
||||||
341,
|
|
||||||
13.333,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.1403,
|
|
||||||
341,
|
|
||||||
16.5553,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.6956,
|
|
||||||
341,
|
|
||||||
16.5553,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
82.2509,
|
|
||||||
341,
|
|
||||||
15.8415,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
396,
|
|
||||||
18.8912,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.7988,
|
|
||||||
396,
|
|
||||||
15.0085,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.8073,
|
|
||||||
396,
|
|
||||||
13.333,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
49.1403,
|
|
||||||
396,
|
|
||||||
16.5553,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.6956,
|
|
||||||
396,
|
|
||||||
16.5553,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
82.2509,
|
|
||||||
396,
|
|
||||||
15.8415,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "62d793eb0343d832087d687b76639e09"
|
|
||||||
}
|
|
||||||
@@ -1,998 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "orders",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 497,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
26,
|
|
||||||
100,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
61,
|
|
||||||
100,
|
|
||||||
436,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
74,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
134,
|
|
||||||
26.7495,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
134,
|
|
||||||
29.1043,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.5575,
|
|
||||||
134,
|
|
||||||
20.6019,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
80.1594,
|
|
||||||
134,
|
|
||||||
26.6515,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
106.8109,
|
|
||||||
134,
|
|
||||||
23.0502,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
129.8611,
|
|
||||||
134,
|
|
||||||
21.2028,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
151.0639,
|
|
||||||
134,
|
|
||||||
17.094,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
165,
|
|
||||||
26.7495,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
165,
|
|
||||||
29.1043,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.5575,
|
|
||||||
165,
|
|
||||||
20.6019,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
80.1594,
|
|
||||||
165,
|
|
||||||
26.6515,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
106.8109,
|
|
||||||
165,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
129.8611,
|
|
||||||
165,
|
|
||||||
21.2028,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
151.0639,
|
|
||||||
165,
|
|
||||||
17.094,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
226,
|
|
||||||
26.7495,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
226,
|
|
||||||
29.1043,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.5575,
|
|
||||||
226,
|
|
||||||
20.6019,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
80.1594,
|
|
||||||
226,
|
|
||||||
26.6515,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
106.8109,
|
|
||||||
226,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
129.8611,
|
|
||||||
226,
|
|
||||||
21.2028,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
151.0639,
|
|
||||||
226,
|
|
||||||
17.094,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
287,
|
|
||||||
26.7495,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
287,
|
|
||||||
29.1043,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.5575,
|
|
||||||
287,
|
|
||||||
20.6019,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
80.1594,
|
|
||||||
287,
|
|
||||||
26.6515,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
106.8109,
|
|
||||||
287,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
129.8611,
|
|
||||||
287,
|
|
||||||
21.2028,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
151.0639,
|
|
||||||
287,
|
|
||||||
17.094,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
348,
|
|
||||||
26.7495,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
348,
|
|
||||||
29.1043,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.5575,
|
|
||||||
348,
|
|
||||||
20.6019,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
80.1594,
|
|
||||||
348,
|
|
||||||
26.6515,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
106.8109,
|
|
||||||
348,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
129.8611,
|
|
||||||
348,
|
|
||||||
21.2028,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
151.0639,
|
|
||||||
348,
|
|
||||||
17.094,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
409,
|
|
||||||
26.7495,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
30.4532,
|
|
||||||
409,
|
|
||||||
29.1043,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.5575,
|
|
||||||
409,
|
|
||||||
20.6019,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
80.1594,
|
|
||||||
409,
|
|
||||||
26.6515,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
106.8109,
|
|
||||||
409,
|
|
||||||
23.0502,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
129.8611,
|
|
||||||
409,
|
|
||||||
21.2028,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
151.0639,
|
|
||||||
409,
|
|
||||||
17.094,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "orders",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 502,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
16.6461,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
16.6461,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
435,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
86,
|
|
||||||
94.837,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
146,
|
|
||||||
15.642,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.2235,
|
|
||||||
146,
|
|
||||||
16.9837,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.2072,
|
|
||||||
146,
|
|
||||||
12.1306,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.3378,
|
|
||||||
146,
|
|
||||||
14.5338,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
61.8716,
|
|
||||||
146,
|
|
||||||
13.5296,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
75.4012,
|
|
||||||
146,
|
|
||||||
12.4745,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.8758,
|
|
||||||
146,
|
|
||||||
9.5427,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
179,
|
|
||||||
15.642,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.2235,
|
|
||||||
179,
|
|
||||||
16.9837,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.2072,
|
|
||||||
179,
|
|
||||||
12.1306,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.3378,
|
|
||||||
179,
|
|
||||||
14.5338,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
61.8716,
|
|
||||||
179,
|
|
||||||
13.5296,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
75.4012,
|
|
||||||
179,
|
|
||||||
12.4745,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.8758,
|
|
||||||
179,
|
|
||||||
9.5427,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
240,
|
|
||||||
15.642,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.2235,
|
|
||||||
240,
|
|
||||||
16.9837,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.2072,
|
|
||||||
240,
|
|
||||||
12.1306,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.3378,
|
|
||||||
240,
|
|
||||||
14.5338,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
61.8716,
|
|
||||||
240,
|
|
||||||
13.5296,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
75.4012,
|
|
||||||
240,
|
|
||||||
12.4745,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.8758,
|
|
||||||
240,
|
|
||||||
9.5427,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
301,
|
|
||||||
15.642,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.2235,
|
|
||||||
301,
|
|
||||||
16.9837,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.2072,
|
|
||||||
301,
|
|
||||||
12.1306,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.3378,
|
|
||||||
301,
|
|
||||||
14.5338,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
61.8716,
|
|
||||||
301,
|
|
||||||
13.5296,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
75.4012,
|
|
||||||
301,
|
|
||||||
12.4745,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.8758,
|
|
||||||
301,
|
|
||||||
9.5427,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
362,
|
|
||||||
15.642,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.2235,
|
|
||||||
362,
|
|
||||||
16.9837,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.2072,
|
|
||||||
362,
|
|
||||||
12.1306,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.3378,
|
|
||||||
362,
|
|
||||||
14.5338,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
61.8716,
|
|
||||||
362,
|
|
||||||
13.5296,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
75.4012,
|
|
||||||
362,
|
|
||||||
12.4745,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.8758,
|
|
||||||
362,
|
|
||||||
9.5427,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
423,
|
|
||||||
15.642,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.2235,
|
|
||||||
423,
|
|
||||||
16.9837,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.2072,
|
|
||||||
423,
|
|
||||||
12.1306,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.3378,
|
|
||||||
423,
|
|
||||||
14.5338,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
61.8716,
|
|
||||||
423,
|
|
||||||
13.5296,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
75.4012,
|
|
||||||
423,
|
|
||||||
12.4745,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.8758,
|
|
||||||
423,
|
|
||||||
9.5427,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "orders",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 470,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
12.3008,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
12.3008,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
403,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
86,
|
|
||||||
96.1847,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
138,
|
|
||||||
16.2243,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.1319,
|
|
||||||
138,
|
|
||||||
17.5044,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.6363,
|
|
||||||
138,
|
|
||||||
12.8891,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.5254,
|
|
||||||
138,
|
|
||||||
13.7535,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.2788,
|
|
||||||
138,
|
|
||||||
14.2178,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
76.4966,
|
|
||||||
138,
|
|
||||||
13.2138,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
89.7104,
|
|
||||||
138,
|
|
||||||
8.382,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
176,
|
|
||||||
16.2243,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.1319,
|
|
||||||
176,
|
|
||||||
17.5044,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.6363,
|
|
||||||
176,
|
|
||||||
12.8891,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.5254,
|
|
||||||
176,
|
|
||||||
13.7535,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.2788,
|
|
||||||
176,
|
|
||||||
14.2178,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
76.4966,
|
|
||||||
176,
|
|
||||||
13.2138,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
89.7104,
|
|
||||||
176,
|
|
||||||
8.382,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
231,
|
|
||||||
16.2243,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.1319,
|
|
||||||
231,
|
|
||||||
17.5044,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.6363,
|
|
||||||
231,
|
|
||||||
12.8891,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.5254,
|
|
||||||
231,
|
|
||||||
13.7535,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.2788,
|
|
||||||
231,
|
|
||||||
14.2178,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
76.4966,
|
|
||||||
231,
|
|
||||||
13.2138,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
89.7104,
|
|
||||||
231,
|
|
||||||
8.382,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
286,
|
|
||||||
16.2243,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.1319,
|
|
||||||
286,
|
|
||||||
17.5044,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.6363,
|
|
||||||
286,
|
|
||||||
12.8891,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.5254,
|
|
||||||
286,
|
|
||||||
13.7535,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.2788,
|
|
||||||
286,
|
|
||||||
14.2178,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
76.4966,
|
|
||||||
286,
|
|
||||||
13.2138,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
89.7104,
|
|
||||||
286,
|
|
||||||
8.382,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
341,
|
|
||||||
16.2243,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.1319,
|
|
||||||
341,
|
|
||||||
17.5044,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.6363,
|
|
||||||
341,
|
|
||||||
12.8891,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.5254,
|
|
||||||
341,
|
|
||||||
13.7535,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.2788,
|
|
||||||
341,
|
|
||||||
14.2178,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
76.4966,
|
|
||||||
341,
|
|
||||||
13.2138,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
89.7104,
|
|
||||||
341,
|
|
||||||
8.382,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
396,
|
|
||||||
16.2243,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.1319,
|
|
||||||
396,
|
|
||||||
17.5044,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.6363,
|
|
||||||
396,
|
|
||||||
12.8891,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.5254,
|
|
||||||
396,
|
|
||||||
13.7535,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
62.2788,
|
|
||||||
396,
|
|
||||||
14.2178,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
76.4966,
|
|
||||||
396,
|
|
||||||
13.2138,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
89.7104,
|
|
||||||
396,
|
|
||||||
8.382,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "677a0002aa805c9f7790bc68c6374bb5"
|
|
||||||
}
|
|
||||||
@@ -1,371 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "project-detail",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 481,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
3.4188,
|
|
||||||
12,
|
|
||||||
5.698,
|
|
||||||
20,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
14.8148,
|
|
||||||
9,
|
|
||||||
50.2315,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
52,
|
|
||||||
48.0057,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.4245,
|
|
||||||
52,
|
|
||||||
48.5755,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
112,
|
|
||||||
100,
|
|
||||||
188,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
125,
|
|
||||||
48.1481,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
154,
|
|
||||||
48.1481,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
56.4103,
|
|
||||||
125,
|
|
||||||
48.1481,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
56.4103,
|
|
||||||
154,
|
|
||||||
48.1481,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
214,
|
|
||||||
48.1481,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
243,
|
|
||||||
48.1481,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
56.4103,
|
|
||||||
214,
|
|
||||||
48.1481,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
56.4103,
|
|
||||||
243,
|
|
||||||
48.1481,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
316,
|
|
||||||
100,
|
|
||||||
165,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
329,
|
|
||||||
92.5926,
|
|
||||||
17,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
357,
|
|
||||||
92.5926,
|
|
||||||
104,
|
|
||||||
8
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "project-detail",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 453,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
1.6304,
|
|
||||||
12,
|
|
||||||
2.7174,
|
|
||||||
20,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
7.0652,
|
|
||||||
7,
|
|
||||||
29.2799,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
78.2375,
|
|
||||||
0,
|
|
||||||
9.1733,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
89.0413,
|
|
||||||
0,
|
|
||||||
10.9587,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
60,
|
|
||||||
100,
|
|
||||||
200,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
79,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
108,
|
|
||||||
46.3315,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
79,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
108,
|
|
||||||
46.3315,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
168,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
197,
|
|
||||||
46.3315,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
168,
|
|
||||||
46.3315,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.087,
|
|
||||||
197,
|
|
||||||
46.3315,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
276,
|
|
||||||
100,
|
|
||||||
177,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
295,
|
|
||||||
94.837,
|
|
||||||
17,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
323,
|
|
||||||
94.837,
|
|
||||||
104,
|
|
||||||
8
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "project-detail",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 404,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0.6024,
|
|
||||||
7,
|
|
||||||
2.008,
|
|
||||||
20,
|
|
||||||
"50%"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.0161,
|
|
||||||
2,
|
|
||||||
21.6365,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.7217,
|
|
||||||
0,
|
|
||||||
6.3771,
|
|
||||||
34,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
92.3036,
|
|
||||||
0,
|
|
||||||
7.6964,
|
|
||||||
34,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
50,
|
|
||||||
100,
|
|
||||||
180,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
69,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
96,
|
|
||||||
47.2892,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
69,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
96,
|
|
||||||
47.2892,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
148,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
175,
|
|
||||||
47.2892,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
148,
|
|
||||||
47.2892,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50.8032,
|
|
||||||
175,
|
|
||||||
47.2892,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
246,
|
|
||||||
100,
|
|
||||||
157,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
265,
|
|
||||||
96.1847,
|
|
||||||
17,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
294,
|
|
||||||
96.1847,
|
|
||||||
84,
|
|
||||||
8
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "ab5e1f108d42c55b0e6382fcaffff793"
|
|
||||||
}
|
|
||||||
@@ -1,746 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "projects",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 497,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
26,
|
|
||||||
100,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
61,
|
|
||||||
100,
|
|
||||||
436,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
74,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
134,
|
|
||||||
34.6065,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.3102,
|
|
||||||
134,
|
|
||||||
31.3568,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.667,
|
|
||||||
134,
|
|
||||||
26.6515,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
96.3186,
|
|
||||||
134,
|
|
||||||
27.7066,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
124.0251,
|
|
||||||
134,
|
|
||||||
22.1287,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
165,
|
|
||||||
34.6065,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.3102,
|
|
||||||
165,
|
|
||||||
31.3568,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.667,
|
|
||||||
165,
|
|
||||||
26.6515,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
96.3186,
|
|
||||||
165,
|
|
||||||
27.7066,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
124.0251,
|
|
||||||
165,
|
|
||||||
22.1287,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
226,
|
|
||||||
34.6065,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.3102,
|
|
||||||
226,
|
|
||||||
31.3568,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.667,
|
|
||||||
226,
|
|
||||||
26.6515,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
96.3186,
|
|
||||||
226,
|
|
||||||
27.7066,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
124.0251,
|
|
||||||
226,
|
|
||||||
22.1287,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
287,
|
|
||||||
34.6065,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.3102,
|
|
||||||
287,
|
|
||||||
31.3568,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.667,
|
|
||||||
287,
|
|
||||||
26.6515,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
96.3186,
|
|
||||||
287,
|
|
||||||
27.7066,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
124.0251,
|
|
||||||
287,
|
|
||||||
22.1287,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
348,
|
|
||||||
34.6065,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.3102,
|
|
||||||
348,
|
|
||||||
31.3568,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.667,
|
|
||||||
348,
|
|
||||||
26.6515,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
96.3186,
|
|
||||||
348,
|
|
||||||
27.7066,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
124.0251,
|
|
||||||
348,
|
|
||||||
22.1287,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
409,
|
|
||||||
34.6065,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.3102,
|
|
||||||
409,
|
|
||||||
31.3568,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.667,
|
|
||||||
409,
|
|
||||||
26.6515,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
96.3186,
|
|
||||||
409,
|
|
||||||
27.7066,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
124.0251,
|
|
||||||
409,
|
|
||||||
22.1287,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "projects",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 502,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
10.9099,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
10.9099,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
435,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
86,
|
|
||||||
94.837,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
146,
|
|
||||||
23.429,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.0105,
|
|
||||||
146,
|
|
||||||
21.2806,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2911,
|
|
||||||
146,
|
|
||||||
18.1704,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.4615,
|
|
||||||
146,
|
|
||||||
17.6694,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.1309,
|
|
||||||
146,
|
|
||||||
14.2875,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
179,
|
|
||||||
23.429,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.0105,
|
|
||||||
179,
|
|
||||||
21.2806,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2911,
|
|
||||||
179,
|
|
||||||
18.1704,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.4615,
|
|
||||||
179,
|
|
||||||
17.6694,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.1309,
|
|
||||||
179,
|
|
||||||
14.2875,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
240,
|
|
||||||
23.429,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.0105,
|
|
||||||
240,
|
|
||||||
21.2806,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2911,
|
|
||||||
240,
|
|
||||||
18.1704,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.4615,
|
|
||||||
240,
|
|
||||||
17.6694,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.1309,
|
|
||||||
240,
|
|
||||||
14.2875,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
301,
|
|
||||||
23.429,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.0105,
|
|
||||||
301,
|
|
||||||
21.2806,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2911,
|
|
||||||
301,
|
|
||||||
18.1704,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.4615,
|
|
||||||
301,
|
|
||||||
17.6694,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.1309,
|
|
||||||
301,
|
|
||||||
14.2875,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
362,
|
|
||||||
23.429,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.0105,
|
|
||||||
362,
|
|
||||||
21.2806,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2911,
|
|
||||||
362,
|
|
||||||
18.1704,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.4615,
|
|
||||||
362,
|
|
||||||
17.6694,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.1309,
|
|
||||||
362,
|
|
||||||
14.2875,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
423,
|
|
||||||
23.429,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.0105,
|
|
||||||
423,
|
|
||||||
21.2806,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.2911,
|
|
||||||
423,
|
|
||||||
18.1704,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.4615,
|
|
||||||
423,
|
|
||||||
17.6694,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
83.1309,
|
|
||||||
423,
|
|
||||||
14.2875,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "projects",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 470,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
8.0619,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
8.0619,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
403,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
86,
|
|
||||||
96.1847,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
138,
|
|
||||||
24.4588,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.3664,
|
|
||||||
138,
|
|
||||||
22.4068,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.7732,
|
|
||||||
138,
|
|
||||||
19.4308,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
68.2041,
|
|
||||||
138,
|
|
||||||
17.2612,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4653,
|
|
||||||
138,
|
|
||||||
12.6271,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
176,
|
|
||||||
24.4588,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.3664,
|
|
||||||
176,
|
|
||||||
22.4068,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.7732,
|
|
||||||
176,
|
|
||||||
19.4308,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
68.2041,
|
|
||||||
176,
|
|
||||||
17.2612,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4653,
|
|
||||||
176,
|
|
||||||
12.6271,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
231,
|
|
||||||
24.4588,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.3664,
|
|
||||||
231,
|
|
||||||
22.4068,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.7732,
|
|
||||||
231,
|
|
||||||
19.4308,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
68.2041,
|
|
||||||
231,
|
|
||||||
17.2612,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4653,
|
|
||||||
231,
|
|
||||||
12.6271,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
286,
|
|
||||||
24.4588,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.3664,
|
|
||||||
286,
|
|
||||||
22.4068,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.7732,
|
|
||||||
286,
|
|
||||||
19.4308,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
68.2041,
|
|
||||||
286,
|
|
||||||
17.2612,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4653,
|
|
||||||
286,
|
|
||||||
12.6271,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
341,
|
|
||||||
24.4588,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.3664,
|
|
||||||
341,
|
|
||||||
22.4068,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.7732,
|
|
||||||
341,
|
|
||||||
19.4308,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
68.2041,
|
|
||||||
341,
|
|
||||||
17.2612,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4653,
|
|
||||||
341,
|
|
||||||
12.6271,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
396,
|
|
||||||
24.4588,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.3664,
|
|
||||||
396,
|
|
||||||
22.4068,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
48.7732,
|
|
||||||
396,
|
|
||||||
19.4308,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
68.2041,
|
|
||||||
396,
|
|
||||||
17.2612,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4653,
|
|
||||||
396,
|
|
||||||
12.6271,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "17f8285c3ca514ddef6d48c1183ed642"
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
"use client"
|
|
||||||
// Auto-generated by `npx boneyard-js build` — do not edit
|
|
||||||
import { registerBones } from 'boneyard-js'
|
|
||||||
import { configureBoneyard } from 'boneyard-js/react'
|
|
||||||
|
|
||||||
import _dash_sessions from './dash-sessions.bones.json'
|
|
||||||
import _attendance_history_fund from './attendance-history-fund.bones.json'
|
|
||||||
import _attendance_history_table from './attendance-history-table.bones.json'
|
|
||||||
import _leave_requests from './leave-requests.bones.json'
|
|
||||||
import _leave_approval from './leave-approval.bones.json'
|
|
||||||
import _attendance_balances from './attendance-balances.bones.json'
|
|
||||||
import _trips_history from './trips-history.bones.json'
|
|
||||||
import _trips_admin from './trips-admin.bones.json'
|
|
||||||
import _vehicles from './vehicles.bones.json'
|
|
||||||
import _offers from './offers.bones.json'
|
|
||||||
import _orders from './orders.bones.json'
|
|
||||||
import _projects from './projects.bones.json'
|
|
||||||
import _offers_customers from './offers-customers.bones.json'
|
|
||||||
import _users from './users.bones.json'
|
|
||||||
import _audit_log_rows from './audit-log-rows.bones.json'
|
|
||||||
import _offer_detail from './offer-detail.bones.json'
|
|
||||||
import _invoice_detail from './invoice-detail.bones.json'
|
|
||||||
import _project_detail from './project-detail.bones.json'
|
|
||||||
import _attendance_create from './attendance-create.bones.json'
|
|
||||||
import _offers_templates from './offers-templates.bones.json'
|
|
||||||
|
|
||||||
configureBoneyard({"color":"#e0e0e0","animate":"shimmer","shimmerColor":"#f0f0f0","speed":"1.2s","shimmerAngle":110})
|
|
||||||
|
|
||||||
registerBones({
|
|
||||||
"dash-sessions": _dash_sessions,
|
|
||||||
"attendance-history-fund": _attendance_history_fund,
|
|
||||||
"attendance-history-table": _attendance_history_table,
|
|
||||||
"leave-requests": _leave_requests,
|
|
||||||
"leave-approval": _leave_approval,
|
|
||||||
"attendance-balances": _attendance_balances,
|
|
||||||
"trips-history": _trips_history,
|
|
||||||
"trips-admin": _trips_admin,
|
|
||||||
"vehicles": _vehicles,
|
|
||||||
"offers": _offers,
|
|
||||||
"orders": _orders,
|
|
||||||
"projects": _projects,
|
|
||||||
"offers-customers": _offers_customers,
|
|
||||||
"users": _users,
|
|
||||||
"audit-log-rows": _audit_log_rows,
|
|
||||||
"offer-detail": _offer_detail,
|
|
||||||
"invoice-detail": _invoice_detail,
|
|
||||||
"project-detail": _project_detail,
|
|
||||||
"attendance-create": _attendance_create,
|
|
||||||
"offers-templates": _offers_templates,
|
|
||||||
})
|
|
||||||
@@ -1,725 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "trips-admin",
|
|
||||||
"viewportWidth": 317,
|
|
||||||
"width": 317,
|
|
||||||
"height": 437,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
26,
|
|
||||||
100,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
61,
|
|
||||||
100,
|
|
||||||
376,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
74,
|
|
||||||
37.8795,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.9805,
|
|
||||||
74,
|
|
||||||
43.4592,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4397,
|
|
||||||
74,
|
|
||||||
31.6739,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
117.1136,
|
|
||||||
74,
|
|
||||||
16.6108,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
133.7244,
|
|
||||||
74,
|
|
||||||
28.1053,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
105,
|
|
||||||
37.8795,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.9805,
|
|
||||||
105,
|
|
||||||
43.4592,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4397,
|
|
||||||
105,
|
|
||||||
31.6739,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
117.1136,
|
|
||||||
105,
|
|
||||||
16.6108,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
133.7244,
|
|
||||||
105,
|
|
||||||
28.1053,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
166,
|
|
||||||
37.8795,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.9805,
|
|
||||||
166,
|
|
||||||
43.4592,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4397,
|
|
||||||
166,
|
|
||||||
31.6739,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
117.1136,
|
|
||||||
166,
|
|
||||||
16.6108,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
133.7244,
|
|
||||||
166,
|
|
||||||
28.1053,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
227,
|
|
||||||
37.8795,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.9805,
|
|
||||||
227,
|
|
||||||
43.4592,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4397,
|
|
||||||
227,
|
|
||||||
31.6739,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
117.1136,
|
|
||||||
227,
|
|
||||||
16.6108,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
133.7244,
|
|
||||||
227,
|
|
||||||
28.1053,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
288,
|
|
||||||
37.8795,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.9805,
|
|
||||||
288,
|
|
||||||
43.4592,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4397,
|
|
||||||
288,
|
|
||||||
31.6739,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
117.1136,
|
|
||||||
288,
|
|
||||||
16.6108,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
133.7244,
|
|
||||||
288,
|
|
||||||
28.1053,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
349,
|
|
||||||
37.8795,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.9805,
|
|
||||||
349,
|
|
||||||
43.4592,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
85.4397,
|
|
||||||
349,
|
|
||||||
31.6739,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
117.1136,
|
|
||||||
349,
|
|
||||||
16.6108,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
133.7244,
|
|
||||||
349,
|
|
||||||
28.1053,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "trips-admin",
|
|
||||||
"viewportWidth": 690,
|
|
||||||
"width": 690,
|
|
||||||
"height": 442,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
16.3202,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
16.3202,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
375,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
86,
|
|
||||||
22.8057,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.5593,
|
|
||||||
86,
|
|
||||||
26.0711,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.6304,
|
|
||||||
86,
|
|
||||||
19.1757,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.8062,
|
|
||||||
86,
|
|
||||||
10.3601,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1662,
|
|
||||||
86,
|
|
||||||
16.0802,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
119,
|
|
||||||
22.8057,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.5593,
|
|
||||||
119,
|
|
||||||
26.0711,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.6304,
|
|
||||||
119,
|
|
||||||
19.1757,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.8062,
|
|
||||||
119,
|
|
||||||
10.3601,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1662,
|
|
||||||
119,
|
|
||||||
16.0802,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
180,
|
|
||||||
22.8057,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.5593,
|
|
||||||
180,
|
|
||||||
26.0711,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.6304,
|
|
||||||
180,
|
|
||||||
19.1757,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.8062,
|
|
||||||
180,
|
|
||||||
10.3601,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1662,
|
|
||||||
180,
|
|
||||||
16.0802,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
241,
|
|
||||||
22.8057,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.5593,
|
|
||||||
241,
|
|
||||||
26.0711,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.6304,
|
|
||||||
241,
|
|
||||||
19.1757,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.8062,
|
|
||||||
241,
|
|
||||||
10.3601,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1662,
|
|
||||||
241,
|
|
||||||
16.0802,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
302,
|
|
||||||
22.8057,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.5593,
|
|
||||||
302,
|
|
||||||
26.0711,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.6304,
|
|
||||||
302,
|
|
||||||
19.1757,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.8062,
|
|
||||||
302,
|
|
||||||
10.3601,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1662,
|
|
||||||
302,
|
|
||||||
16.0802,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
363,
|
|
||||||
22.8057,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.5593,
|
|
||||||
363,
|
|
||||||
26.0711,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
51.6304,
|
|
||||||
363,
|
|
||||||
19.1757,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.8062,
|
|
||||||
363,
|
|
||||||
10.3601,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1662,
|
|
||||||
363,
|
|
||||||
16.0802,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "trips-admin",
|
|
||||||
"viewportWidth": 950,
|
|
||||||
"width": 950,
|
|
||||||
"height": 418,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
11.8536,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
11.8536,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
351,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2,
|
|
||||||
86,
|
|
||||||
23.523,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.523,
|
|
||||||
86,
|
|
||||||
26.574,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.097,
|
|
||||||
86,
|
|
||||||
20.1382,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.2352,
|
|
||||||
86,
|
|
||||||
11.9046,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.1398,
|
|
||||||
86,
|
|
||||||
13.8602,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2,
|
|
||||||
124,
|
|
||||||
23.523,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.523,
|
|
||||||
124,
|
|
||||||
26.574,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.097,
|
|
||||||
124,
|
|
||||||
20.1382,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.2352,
|
|
||||||
124,
|
|
||||||
11.9046,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.1398,
|
|
||||||
124,
|
|
||||||
13.8602,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2,
|
|
||||||
179,
|
|
||||||
23.523,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.523,
|
|
||||||
179,
|
|
||||||
26.574,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.097,
|
|
||||||
179,
|
|
||||||
20.1382,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.2352,
|
|
||||||
179,
|
|
||||||
11.9046,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.1398,
|
|
||||||
179,
|
|
||||||
13.8602,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2,
|
|
||||||
234,
|
|
||||||
23.523,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.523,
|
|
||||||
234,
|
|
||||||
26.574,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.097,
|
|
||||||
234,
|
|
||||||
20.1382,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.2352,
|
|
||||||
234,
|
|
||||||
11.9046,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.1398,
|
|
||||||
234,
|
|
||||||
13.8602,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2,
|
|
||||||
289,
|
|
||||||
23.523,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.523,
|
|
||||||
289,
|
|
||||||
26.574,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.097,
|
|
||||||
289,
|
|
||||||
20.1382,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.2352,
|
|
||||||
289,
|
|
||||||
11.9046,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.1398,
|
|
||||||
289,
|
|
||||||
13.8602,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2,
|
|
||||||
344,
|
|
||||||
23.523,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
25.523,
|
|
||||||
344,
|
|
||||||
26.574,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.097,
|
|
||||||
344,
|
|
||||||
20.1382,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
72.2352,
|
|
||||||
344,
|
|
||||||
11.9046,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.1398,
|
|
||||||
344,
|
|
||||||
13.8602,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "39a325f430c84bb51960a684759a8f0c"
|
|
||||||
}
|
|
||||||
@@ -1,725 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "trips-history",
|
|
||||||
"viewportWidth": 317,
|
|
||||||
"width": 317,
|
|
||||||
"height": 300,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
26,
|
|
||||||
100,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
61,
|
|
||||||
100,
|
|
||||||
239,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
74,
|
|
||||||
35.2326,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
39.3336,
|
|
||||||
74,
|
|
||||||
40.4278,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
79.7614,
|
|
||||||
74,
|
|
||||||
29.4657,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.2271,
|
|
||||||
74,
|
|
||||||
37.1402,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
146.3673,
|
|
||||||
74,
|
|
||||||
15.4623,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
105,
|
|
||||||
35.2326,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
39.3336,
|
|
||||||
105,
|
|
||||||
40.4278,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
79.7614,
|
|
||||||
105,
|
|
||||||
29.4657,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.2271,
|
|
||||||
105,
|
|
||||||
37.1402,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
146.3673,
|
|
||||||
105,
|
|
||||||
15.4623,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
138,
|
|
||||||
35.2326,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
39.3336,
|
|
||||||
138,
|
|
||||||
40.4278,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
79.7614,
|
|
||||||
138,
|
|
||||||
29.4657,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.2271,
|
|
||||||
138,
|
|
||||||
37.1402,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
146.3673,
|
|
||||||
138,
|
|
||||||
15.4623,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
172,
|
|
||||||
35.2326,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
39.3336,
|
|
||||||
172,
|
|
||||||
40.4278,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
79.7614,
|
|
||||||
172,
|
|
||||||
29.4657,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.2271,
|
|
||||||
172,
|
|
||||||
37.1402,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
146.3673,
|
|
||||||
172,
|
|
||||||
15.4623,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
205,
|
|
||||||
35.2326,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
39.3336,
|
|
||||||
205,
|
|
||||||
40.4278,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
79.7614,
|
|
||||||
205,
|
|
||||||
29.4657,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.2271,
|
|
||||||
205,
|
|
||||||
37.1402,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
146.3673,
|
|
||||||
205,
|
|
||||||
15.4623,
|
|
||||||
34,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
4.1009,
|
|
||||||
239,
|
|
||||||
35.2326,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
39.3336,
|
|
||||||
239,
|
|
||||||
40.4278,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
79.7614,
|
|
||||||
239,
|
|
||||||
29.4657,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
109.2271,
|
|
||||||
239,
|
|
||||||
37.1402,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
146.3673,
|
|
||||||
239,
|
|
||||||
15.4623,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "trips-history",
|
|
||||||
"viewportWidth": 690,
|
|
||||||
"width": 690,
|
|
||||||
"height": 312,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
16.6033,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
16.6033,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
245,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
86,
|
|
||||||
21.0417,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.7953,
|
|
||||||
86,
|
|
||||||
24.0534,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.8487,
|
|
||||||
86,
|
|
||||||
17.6925,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.5412,
|
|
||||||
86,
|
|
||||||
22.1445,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.6857,
|
|
||||||
86,
|
|
||||||
9.5607,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
119,
|
|
||||||
21.0417,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.7953,
|
|
||||||
119,
|
|
||||||
24.0534,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.8487,
|
|
||||||
119,
|
|
||||||
17.6925,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.5412,
|
|
||||||
119,
|
|
||||||
22.1445,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.6857,
|
|
||||||
119,
|
|
||||||
9.5607,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
154,
|
|
||||||
21.0417,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.7953,
|
|
||||||
154,
|
|
||||||
24.0534,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.8487,
|
|
||||||
154,
|
|
||||||
17.6925,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.5412,
|
|
||||||
154,
|
|
||||||
22.1445,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.6857,
|
|
||||||
154,
|
|
||||||
9.5607,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
189,
|
|
||||||
21.0417,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.7953,
|
|
||||||
189,
|
|
||||||
24.0534,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.8487,
|
|
||||||
189,
|
|
||||||
17.6925,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.5412,
|
|
||||||
189,
|
|
||||||
22.1445,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.6857,
|
|
||||||
189,
|
|
||||||
9.5607,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
224,
|
|
||||||
21.0417,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.7953,
|
|
||||||
224,
|
|
||||||
24.0534,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.8487,
|
|
||||||
224,
|
|
||||||
17.6925,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.5412,
|
|
||||||
224,
|
|
||||||
22.1445,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.6857,
|
|
||||||
224,
|
|
||||||
9.5607,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.7536,
|
|
||||||
259,
|
|
||||||
21.0417,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.7953,
|
|
||||||
259,
|
|
||||||
24.0534,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.8487,
|
|
||||||
259,
|
|
||||||
17.6925,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.5412,
|
|
||||||
259,
|
|
||||||
22.1445,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.6857,
|
|
||||||
259,
|
|
||||||
9.5607,
|
|
||||||
35,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "trips-history",
|
|
||||||
"viewportWidth": 958,
|
|
||||||
"width": 958,
|
|
||||||
"height": 355,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
11.9585,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
11.9585,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
288,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9833,
|
|
||||||
86,
|
|
||||||
21.1541,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.1374,
|
|
||||||
86,
|
|
||||||
23.8974,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.0348,
|
|
||||||
86,
|
|
||||||
18.1106,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.1455,
|
|
||||||
86,
|
|
||||||
22.1604,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.3059,
|
|
||||||
86,
|
|
||||||
10.7108,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9833,
|
|
||||||
124,
|
|
||||||
21.1541,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.1374,
|
|
||||||
124,
|
|
||||||
23.8974,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.0348,
|
|
||||||
124,
|
|
||||||
18.1106,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.1455,
|
|
||||||
124,
|
|
||||||
22.1604,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.3059,
|
|
||||||
124,
|
|
||||||
10.7108,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9833,
|
|
||||||
167,
|
|
||||||
21.1541,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.1374,
|
|
||||||
167,
|
|
||||||
23.8974,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.0348,
|
|
||||||
167,
|
|
||||||
18.1106,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.1455,
|
|
||||||
167,
|
|
||||||
22.1604,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.3059,
|
|
||||||
167,
|
|
||||||
10.7108,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9833,
|
|
||||||
209,
|
|
||||||
21.1541,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.1374,
|
|
||||||
209,
|
|
||||||
23.8974,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.0348,
|
|
||||||
209,
|
|
||||||
18.1106,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.1455,
|
|
||||||
209,
|
|
||||||
22.1604,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.3059,
|
|
||||||
209,
|
|
||||||
10.7108,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9833,
|
|
||||||
252,
|
|
||||||
21.1541,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.1374,
|
|
||||||
252,
|
|
||||||
23.8974,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.0348,
|
|
||||||
252,
|
|
||||||
18.1106,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.1455,
|
|
||||||
252,
|
|
||||||
22.1604,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.3059,
|
|
||||||
252,
|
|
||||||
10.7108,
|
|
||||||
43,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9833,
|
|
||||||
294,
|
|
||||||
21.1541,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
23.1374,
|
|
||||||
294,
|
|
||||||
23.8974,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.0348,
|
|
||||||
294,
|
|
||||||
18.1106,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
65.1455,
|
|
||||||
294,
|
|
||||||
22.1604,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.3059,
|
|
||||||
294,
|
|
||||||
10.7108,
|
|
||||||
42,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "6b54a0afbb4863895e318916b1fdca67"
|
|
||||||
}
|
|
||||||
@@ -1,767 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "users",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 549,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
26,
|
|
||||||
100,
|
|
||||||
19,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
53,
|
|
||||||
100,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
113,
|
|
||||||
100,
|
|
||||||
436,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
126,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
186,
|
|
||||||
37.362,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.0657,
|
|
||||||
186,
|
|
||||||
26.429,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.4947,
|
|
||||||
186,
|
|
||||||
36.1779,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
103.6725,
|
|
||||||
186,
|
|
||||||
23.62,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
127.2926,
|
|
||||||
186,
|
|
||||||
18.8613,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
217,
|
|
||||||
37.362,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.0657,
|
|
||||||
217,
|
|
||||||
26.429,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.4947,
|
|
||||||
217,
|
|
||||||
36.1779,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
103.6725,
|
|
||||||
217,
|
|
||||||
23.62,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
127.2926,
|
|
||||||
217,
|
|
||||||
18.8613,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
278,
|
|
||||||
37.362,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.0657,
|
|
||||||
278,
|
|
||||||
26.429,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.4947,
|
|
||||||
278,
|
|
||||||
36.1779,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
103.6725,
|
|
||||||
278,
|
|
||||||
23.62,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
127.2926,
|
|
||||||
278,
|
|
||||||
18.8613,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
339,
|
|
||||||
37.362,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.0657,
|
|
||||||
339,
|
|
||||||
26.429,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.4947,
|
|
||||||
339,
|
|
||||||
36.1779,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
103.6725,
|
|
||||||
339,
|
|
||||||
23.62,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
127.2926,
|
|
||||||
339,
|
|
||||||
18.8613,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
400,
|
|
||||||
37.362,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.0657,
|
|
||||||
400,
|
|
||||||
26.429,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.4947,
|
|
||||||
400,
|
|
||||||
36.1779,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
103.6725,
|
|
||||||
400,
|
|
||||||
23.62,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
127.2926,
|
|
||||||
400,
|
|
||||||
18.8613,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
461,
|
|
||||||
37.362,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
41.0657,
|
|
||||||
461,
|
|
||||||
26.429,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.4947,
|
|
||||||
461,
|
|
||||||
36.1779,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
103.6725,
|
|
||||||
461,
|
|
||||||
23.62,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
127.2926,
|
|
||||||
461,
|
|
||||||
18.8613,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "users",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 502,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
12.6741,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
12.6741,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.2479,
|
|
||||||
4,
|
|
||||||
18.7521,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
435,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
86,
|
|
||||||
94.837,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
146,
|
|
||||||
24.3079,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.8894,
|
|
||||||
146,
|
|
||||||
18.6481,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.5375,
|
|
||||||
146,
|
|
||||||
23.5628,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.1003,
|
|
||||||
146,
|
|
||||||
15.6568,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.7571,
|
|
||||||
146,
|
|
||||||
12.6613,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
179,
|
|
||||||
24.3079,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.8894,
|
|
||||||
179,
|
|
||||||
18.6481,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.5375,
|
|
||||||
179,
|
|
||||||
23.5628,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.1003,
|
|
||||||
179,
|
|
||||||
15.6568,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.7571,
|
|
||||||
179,
|
|
||||||
12.6613,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
240,
|
|
||||||
24.3079,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.8894,
|
|
||||||
240,
|
|
||||||
18.6481,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.5375,
|
|
||||||
240,
|
|
||||||
23.5628,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.1003,
|
|
||||||
240,
|
|
||||||
15.6568,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.7571,
|
|
||||||
240,
|
|
||||||
12.6613,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
301,
|
|
||||||
24.3079,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.8894,
|
|
||||||
301,
|
|
||||||
18.6481,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.5375,
|
|
||||||
301,
|
|
||||||
23.5628,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.1003,
|
|
||||||
301,
|
|
||||||
15.6568,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.7571,
|
|
||||||
301,
|
|
||||||
12.6613,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
362,
|
|
||||||
24.3079,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.8894,
|
|
||||||
362,
|
|
||||||
18.6481,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.5375,
|
|
||||||
362,
|
|
||||||
23.5628,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.1003,
|
|
||||||
362,
|
|
||||||
15.6568,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.7571,
|
|
||||||
362,
|
|
||||||
12.6613,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
423,
|
|
||||||
24.3079,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
26.8894,
|
|
||||||
423,
|
|
||||||
18.6481,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
45.5375,
|
|
||||||
423,
|
|
||||||
23.5628,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
69.1003,
|
|
||||||
423,
|
|
||||||
15.6568,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
84.7571,
|
|
||||||
423,
|
|
||||||
12.6613,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "users",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 505,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
9.3656,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
30,
|
|
||||||
9.3656,
|
|
||||||
21,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
86.5446,
|
|
||||||
10,
|
|
||||||
13.4554,
|
|
||||||
32,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
67,
|
|
||||||
100,
|
|
||||||
438,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
86,
|
|
||||||
96.1847,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
138,
|
|
||||||
25.3655,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.2732,
|
|
||||||
138,
|
|
||||||
20.4302,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.7033,
|
|
||||||
138,
|
|
||||||
22.8571,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.5604,
|
|
||||||
138,
|
|
||||||
15.9011,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
86.4615,
|
|
||||||
138,
|
|
||||||
11.6309,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
176,
|
|
||||||
25.3655,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.2732,
|
|
||||||
176,
|
|
||||||
20.4302,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.7033,
|
|
||||||
176,
|
|
||||||
22.8571,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.5604,
|
|
||||||
176,
|
|
||||||
15.9011,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
86.4615,
|
|
||||||
176,
|
|
||||||
11.6309,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
238,
|
|
||||||
25.3655,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.2732,
|
|
||||||
238,
|
|
||||||
20.4302,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.7033,
|
|
||||||
238,
|
|
||||||
22.8571,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.5604,
|
|
||||||
238,
|
|
||||||
15.9011,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
86.4615,
|
|
||||||
238,
|
|
||||||
11.6309,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
300,
|
|
||||||
25.3655,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.2732,
|
|
||||||
300,
|
|
||||||
20.4302,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.7033,
|
|
||||||
300,
|
|
||||||
22.8571,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.5604,
|
|
||||||
300,
|
|
||||||
15.9011,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
86.4615,
|
|
||||||
300,
|
|
||||||
11.6309,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
362,
|
|
||||||
25.3655,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.2732,
|
|
||||||
362,
|
|
||||||
20.4302,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.7033,
|
|
||||||
362,
|
|
||||||
22.8571,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.5604,
|
|
||||||
362,
|
|
||||||
15.9011,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
86.4615,
|
|
||||||
362,
|
|
||||||
11.6309,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
424,
|
|
||||||
25.3655,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.2732,
|
|
||||||
424,
|
|
||||||
20.4302,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
47.7033,
|
|
||||||
424,
|
|
||||||
22.8571,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
70.5604,
|
|
||||||
424,
|
|
||||||
15.9011,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
86.4615,
|
|
||||||
424,
|
|
||||||
11.6309,
|
|
||||||
62,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "53e8df6c8f8bf975b3b88bfca3bbd804"
|
|
||||||
}
|
|
||||||
@@ -1,746 +0,0 @@
|
|||||||
{
|
|
||||||
"breakpoints": {
|
|
||||||
"375": {
|
|
||||||
"name": "vehicles",
|
|
||||||
"viewportWidth": 351,
|
|
||||||
"width": 351,
|
|
||||||
"height": 530,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
100,
|
|
||||||
22,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
34,
|
|
||||||
100,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
94,
|
|
||||||
100,
|
|
||||||
436,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
107,
|
|
||||||
92.5926,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
167,
|
|
||||||
23.9583,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.662,
|
|
||||||
167,
|
|
||||||
24.4168,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.0789,
|
|
||||||
167,
|
|
||||||
29.042,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1209,
|
|
||||||
167,
|
|
||||||
18.8435,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
99.9644,
|
|
||||||
167,
|
|
||||||
46.1895,
|
|
||||||
31,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
197,
|
|
||||||
23.9583,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.662,
|
|
||||||
197,
|
|
||||||
24.4168,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.0789,
|
|
||||||
197,
|
|
||||||
29.042,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1209,
|
|
||||||
197,
|
|
||||||
18.8435,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
99.9644,
|
|
||||||
197,
|
|
||||||
46.1895,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
258,
|
|
||||||
23.9583,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.662,
|
|
||||||
258,
|
|
||||||
24.4168,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.0789,
|
|
||||||
258,
|
|
||||||
29.042,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1209,
|
|
||||||
258,
|
|
||||||
18.8435,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
99.9644,
|
|
||||||
258,
|
|
||||||
46.1895,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
319,
|
|
||||||
23.9583,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.662,
|
|
||||||
319,
|
|
||||||
24.4168,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.0789,
|
|
||||||
319,
|
|
||||||
29.042,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1209,
|
|
||||||
319,
|
|
||||||
18.8435,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
99.9644,
|
|
||||||
319,
|
|
||||||
46.1895,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
380,
|
|
||||||
23.9583,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.662,
|
|
||||||
380,
|
|
||||||
24.4168,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.0789,
|
|
||||||
380,
|
|
||||||
29.042,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1209,
|
|
||||||
380,
|
|
||||||
18.8435,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
99.9644,
|
|
||||||
380,
|
|
||||||
46.1895,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
3.7037,
|
|
||||||
441,
|
|
||||||
23.9583,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
27.662,
|
|
||||||
441,
|
|
||||||
24.4168,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
52.0789,
|
|
||||||
441,
|
|
||||||
29.042,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
81.1209,
|
|
||||||
441,
|
|
||||||
18.8435,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
99.9644,
|
|
||||||
441,
|
|
||||||
46.1895,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"768": {
|
|
||||||
"name": "vehicles",
|
|
||||||
"viewportWidth": 736,
|
|
||||||
"width": 736,
|
|
||||||
"height": 495,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
7,
|
|
||||||
10.1478,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
82.6427,
|
|
||||||
0,
|
|
||||||
17.3573,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
60,
|
|
||||||
100,
|
|
||||||
435,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
79,
|
|
||||||
94.837,
|
|
||||||
44,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
139,
|
|
||||||
16.4126,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.9941,
|
|
||||||
139,
|
|
||||||
16.5039,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.498,
|
|
||||||
139,
|
|
||||||
19.5058,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
55.0038,
|
|
||||||
139,
|
|
||||||
12.8843,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.8881,
|
|
||||||
139,
|
|
||||||
29.5304,
|
|
||||||
33,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
172,
|
|
||||||
16.4126,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.9941,
|
|
||||||
172,
|
|
||||||
16.5039,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.498,
|
|
||||||
172,
|
|
||||||
19.5058,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
55.0038,
|
|
||||||
172,
|
|
||||||
12.8843,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.8881,
|
|
||||||
172,
|
|
||||||
29.5304,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
233,
|
|
||||||
16.4126,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.9941,
|
|
||||||
233,
|
|
||||||
16.5039,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.498,
|
|
||||||
233,
|
|
||||||
19.5058,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
55.0038,
|
|
||||||
233,
|
|
||||||
12.8843,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.8881,
|
|
||||||
233,
|
|
||||||
29.5304,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
294,
|
|
||||||
16.4126,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.9941,
|
|
||||||
294,
|
|
||||||
16.5039,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.498,
|
|
||||||
294,
|
|
||||||
19.5058,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
55.0038,
|
|
||||||
294,
|
|
||||||
12.8843,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.8881,
|
|
||||||
294,
|
|
||||||
29.5304,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
355,
|
|
||||||
16.4126,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.9941,
|
|
||||||
355,
|
|
||||||
16.5039,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.498,
|
|
||||||
355,
|
|
||||||
19.5058,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
55.0038,
|
|
||||||
355,
|
|
||||||
12.8843,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.8881,
|
|
||||||
355,
|
|
||||||
29.5304,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
2.5815,
|
|
||||||
416,
|
|
||||||
16.4126,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
18.9941,
|
|
||||||
416,
|
|
||||||
16.5039,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
35.498,
|
|
||||||
416,
|
|
||||||
19.5058,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
55.0038,
|
|
||||||
416,
|
|
||||||
12.8843,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
67.8881,
|
|
||||||
416,
|
|
||||||
29.5304,
|
|
||||||
61,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"1280": {
|
|
||||||
"name": "vehicles",
|
|
||||||
"viewportWidth": 996,
|
|
||||||
"width": 996,
|
|
||||||
"height": 451,
|
|
||||||
"bones": [
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
7.4987,
|
|
||||||
26,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
87.5753,
|
|
||||||
0,
|
|
||||||
12.4247,
|
|
||||||
32,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
0,
|
|
||||||
48,
|
|
||||||
100,
|
|
||||||
403,
|
|
||||||
10,
|
|
||||||
true
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
67,
|
|
||||||
96.1847,
|
|
||||||
36,
|
|
||||||
8
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
119,
|
|
||||||
18.3531,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2607,
|
|
||||||
119,
|
|
||||||
18.2762,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.537,
|
|
||||||
119,
|
|
||||||
21.1785,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.7154,
|
|
||||||
119,
|
|
||||||
14.7841,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
74.4996,
|
|
||||||
119,
|
|
||||||
23.5928,
|
|
||||||
38,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
157,
|
|
||||||
18.3531,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2607,
|
|
||||||
157,
|
|
||||||
18.2762,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.537,
|
|
||||||
157,
|
|
||||||
21.1785,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.7154,
|
|
||||||
157,
|
|
||||||
14.7841,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
74.4996,
|
|
||||||
157,
|
|
||||||
23.5928,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
212,
|
|
||||||
18.3531,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2607,
|
|
||||||
212,
|
|
||||||
18.2762,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.537,
|
|
||||||
212,
|
|
||||||
21.1785,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.7154,
|
|
||||||
212,
|
|
||||||
14.7841,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
74.4996,
|
|
||||||
212,
|
|
||||||
23.5928,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
267,
|
|
||||||
18.3531,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2607,
|
|
||||||
267,
|
|
||||||
18.2762,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.537,
|
|
||||||
267,
|
|
||||||
21.1785,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.7154,
|
|
||||||
267,
|
|
||||||
14.7841,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
74.4996,
|
|
||||||
267,
|
|
||||||
23.5928,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
322,
|
|
||||||
18.3531,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2607,
|
|
||||||
322,
|
|
||||||
18.2762,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.537,
|
|
||||||
322,
|
|
||||||
21.1785,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.7154,
|
|
||||||
322,
|
|
||||||
14.7841,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
74.4996,
|
|
||||||
322,
|
|
||||||
23.5928,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
1.9076,
|
|
||||||
377,
|
|
||||||
18.3531,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
20.2607,
|
|
||||||
377,
|
|
||||||
18.2762,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
38.537,
|
|
||||||
377,
|
|
||||||
21.1785,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
59.7154,
|
|
||||||
377,
|
|
||||||
14.7841,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
[
|
|
||||||
74.4996,
|
|
||||||
377,
|
|
||||||
23.5928,
|
|
||||||
55,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"_hash": "567bad6080dc9ba9767c6e40a88559b9"
|
|
||||||
}
|
|
||||||
@@ -41,6 +41,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Danger card — highlighted border for alert/important cards */
|
||||||
|
.admin-card-danger {
|
||||||
|
border: 2px solid var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================================
|
/* ============================================================================
|
||||||
Badges
|
Badges
|
||||||
============================================================================ */
|
============================================================================ */
|
||||||
@@ -123,6 +128,16 @@
|
|||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.admin-badge-incoming {
|
||||||
|
background: var(--success-soft);
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-badge-outgoing {
|
||||||
|
background: var(--info-soft);
|
||||||
|
color: var(--info);
|
||||||
|
}
|
||||||
|
|
||||||
/* Status Badges - Leave Requests */
|
/* Status Badges - Leave Requests */
|
||||||
.badge-pending {
|
.badge-pending {
|
||||||
background: color-mix(in srgb, var(--warning) 15%, transparent);
|
background: color-mix(in srgb, var(--warning) 15%, transparent);
|
||||||
@@ -716,6 +731,7 @@
|
|||||||
.admin-kpi-grid {
|
.admin-kpi-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 0.875rem;
|
gap: 0.875rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-kpi-4 {
|
.admin-kpi-4 {
|
||||||
@@ -923,3 +939,163 @@
|
|||||||
max-height: 80px;
|
max-height: 80px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Search Bar & Filters
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
.admin-search-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search-bar .admin-form-input,
|
||||||
|
.admin-search-bar .admin-form-select {
|
||||||
|
flex: 1 1 140px;
|
||||||
|
min-width: 140px;
|
||||||
|
max-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search-bar .react-datepicker-wrapper {
|
||||||
|
flex: 1 1 140px;
|
||||||
|
min-width: 140px;
|
||||||
|
max-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search-bar .admin-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search {
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1 200px;
|
||||||
|
min-width: 180px;
|
||||||
|
max-width: 320px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search svg {
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search .admin-form-input {
|
||||||
|
padding-left: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.admin-search-bar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search-bar .admin-form-input,
|
||||||
|
.admin-search-bar .admin-form-select {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search-bar .react-datepicker-wrapper {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search {
|
||||||
|
max-width: 100%;
|
||||||
|
flex: 1 1 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.admin-search-bar .admin-form-input,
|
||||||
|
.admin-search-bar .admin-form-select {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-search-bar .react-datepicker-wrapper {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Item Picker (Warehouse)
|
||||||
|
============================================================================ */
|
||||||
|
|
||||||
|
.admin-item-picker-list {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
max-height: 240px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overscroll-behavior: contain;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-top: none;
|
||||||
|
border-radius: 0 0 var(--border-radius-sm) var(--border-radius-sm);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
padding: 4px 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-item-picker-list::-webkit-scrollbar {
|
||||||
|
width: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-item-picker-list::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-item-picker-list::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--border-color);
|
||||||
|
border-radius: 99px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-item-picker-list::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-item-picker-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background var(--transition);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-item-picker-item:hover,
|
||||||
|
.admin-item-picker-item.active {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-item-picker-name {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-primary);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-item-picker-number {
|
||||||
|
font-size: 11.5px;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-item-picker-qty {
|
||||||
|
font-size: 11.5px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { forwardRef, useMemo } from "react";
|
import { forwardRef } from "react";
|
||||||
import DatePicker, { registerLocale } from "react-datepicker";
|
import DatePicker, { registerLocale } from "react-datepicker";
|
||||||
import { cs } from "date-fns/locale";
|
import { cs } from "date-fns/locale";
|
||||||
import { parse, format } from "date-fns";
|
import { parse, format } from "date-fns";
|
||||||
@@ -124,7 +124,7 @@ export default function AdminDatePicker({
|
|||||||
disabled,
|
disabled,
|
||||||
placeholder,
|
placeholder,
|
||||||
}: AdminDatePickerProps) {
|
}: AdminDatePickerProps) {
|
||||||
const useNative = useMemo(() => isTouchDevice(), []);
|
const useNative = isTouchDevice();
|
||||||
|
|
||||||
if (useNative) {
|
if (useNative) {
|
||||||
return (
|
return (
|
||||||
@@ -178,15 +178,12 @@ export default function AdminDatePicker({
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const customInput = useMemo(
|
const customInput = (
|
||||||
() => (
|
<CustomInput
|
||||||
<CustomInput
|
required={required}
|
||||||
required={required}
|
placeholder={placeholder}
|
||||||
placeholder={placeholder}
|
disabled={disabled}
|
||||||
disabled={disabled}
|
/>
|
||||||
/>
|
|
||||||
),
|
|
||||||
[required, placeholder, disabled],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const commonProps = {
|
const commonProps = {
|
||||||
@@ -194,6 +191,7 @@ export default function AdminDatePicker({
|
|||||||
onChange: handleChange,
|
onChange: handleChange,
|
||||||
locale: "cs",
|
locale: "cs",
|
||||||
customInput,
|
customInput,
|
||||||
|
placeholderText: placeholder,
|
||||||
minDate: parseMinMax(minDate),
|
minDate: parseMinMax(minDate),
|
||||||
maxDate: parseMinMax(maxDate),
|
maxDate: parseMinMax(maxDate),
|
||||||
popperPlacement: "bottom-start" as const,
|
popperPlacement: "bottom-start" as const,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
formatDate,
|
formatDate,
|
||||||
formatDatetime,
|
formatDatetime,
|
||||||
@@ -8,34 +8,13 @@ import {
|
|||||||
getLeaveTypeName,
|
getLeaveTypeName,
|
||||||
getLeaveTypeBadgeClass,
|
getLeaveTypeBadgeClass,
|
||||||
} from "../utils/attendanceHelpers";
|
} from "../utils/attendanceHelpers";
|
||||||
|
import type { AttendanceRecord as HookAttendanceRecord } from "../hooks/useAttendanceAdmin";
|
||||||
|
|
||||||
interface ProjectLog {
|
interface AttendanceRecord extends HookAttendanceRecord {
|
||||||
id?: number;
|
|
||||||
project_id?: number;
|
|
||||||
project_name?: string;
|
|
||||||
started_at?: string;
|
|
||||||
ended_at?: string | null;
|
|
||||||
hours?: string | number | null;
|
|
||||||
minutes?: string | number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AttendanceRecord {
|
|
||||||
id: number;
|
|
||||||
shift_date: string;
|
|
||||||
user_name: string;
|
|
||||||
leave_type?: string;
|
|
||||||
leave_hours?: number;
|
|
||||||
arrival_time?: string | null;
|
|
||||||
departure_time?: string | null;
|
|
||||||
break_start?: string | null;
|
|
||||||
break_end?: string | null;
|
|
||||||
arrival_lat?: number | string | null;
|
arrival_lat?: number | string | null;
|
||||||
arrival_lng?: number | string | null;
|
arrival_lng?: number | string | null;
|
||||||
departure_lat?: number | string | null;
|
departure_lat?: number | string | null;
|
||||||
departure_lng?: number | string | null;
|
departure_lng?: number | string | null;
|
||||||
project_name?: string;
|
|
||||||
project_logs?: ProjectLog[];
|
|
||||||
notes?: string | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AttendanceShiftTableProps {
|
interface AttendanceShiftTableProps {
|
||||||
@@ -51,7 +30,7 @@ function formatBreak(record: AttendanceRecord): string {
|
|||||||
if (record.break_start) {
|
if (record.break_start) {
|
||||||
return `${formatTime(record.break_start)} - ?`;
|
return `${formatTime(record.break_start)} - ?`;
|
||||||
}
|
}
|
||||||
return "\u2014";
|
return "—";
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderProjectCell(record: AttendanceRecord): React.ReactNode {
|
function renderProjectCell(record: AttendanceRecord): React.ReactNode {
|
||||||
@@ -97,7 +76,7 @@ function renderProjectCell(record: AttendanceRecord): React.ReactNode {
|
|||||||
>
|
>
|
||||||
{log.project_name || `#${log.project_id}`}{" "}
|
{log.project_name || `#${log.project_id}`}{" "}
|
||||||
{durationValid
|
{durationValid
|
||||||
? `(${h}:${String(m).padStart(2, "0")}h${isActive ? " \u25B8" : ""})`
|
? `(${h}:${String(m).padStart(2, "0")}h${isActive ? " ▸" : ""})`
|
||||||
: "—"}
|
: "—"}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -115,7 +94,7 @@ function renderProjectCell(record: AttendanceRecord): React.ReactNode {
|
|||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return "\u2014";
|
return "—";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AttendanceShiftTable({
|
export default function AttendanceShiftTable({
|
||||||
@@ -156,7 +135,10 @@ export default function AttendanceShiftTable({
|
|||||||
const workMinutes = isLeave
|
const workMinutes = isLeave
|
||||||
? (record.leave_hours != null ? Number(record.leave_hours) : 8) *
|
? (record.leave_hours != null ? Number(record.leave_hours) : 8) *
|
||||||
60
|
60
|
||||||
: calculateWorkMinutes(record);
|
: calculateWorkMinutes({
|
||||||
|
...record,
|
||||||
|
notes: record.notes ?? undefined,
|
||||||
|
});
|
||||||
const hasLocation =
|
const hasLocation =
|
||||||
(record.arrival_lat && record.arrival_lng) ||
|
(record.arrival_lat && record.arrival_lng) ||
|
||||||
(record.departure_lat && record.departure_lng);
|
(record.departure_lat && record.departure_lng);
|
||||||
@@ -173,18 +155,16 @@ export default function AttendanceShiftTable({
|
|||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="admin-mono">
|
<td className="admin-mono">
|
||||||
{isLeave ? "\u2014" : formatDatetime(record.arrival_time)}
|
{isLeave ? "—" : formatDatetime(record.arrival_time)}
|
||||||
</td>
|
</td>
|
||||||
<td className="admin-mono">
|
<td className="admin-mono">
|
||||||
{isLeave ? "\u2014" : formatBreak(record)}
|
{isLeave ? "—" : formatBreak(record)}
|
||||||
</td>
|
</td>
|
||||||
<td className="admin-mono">
|
<td className="admin-mono">
|
||||||
{isLeave ? "\u2014" : formatDatetime(record.departure_time)}
|
{isLeave ? "—" : formatDatetime(record.departure_time)}
|
||||||
</td>
|
</td>
|
||||||
<td className="admin-mono">
|
<td className="admin-mono">
|
||||||
{workMinutes > 0
|
{workMinutes > 0 ? `${formatMinutes(workMinutes)} h` : "—"}
|
||||||
? `${formatMinutes(workMinutes)} h`
|
|
||||||
: "\u2014"}
|
|
||||||
</td>
|
</td>
|
||||||
<td>{renderProjectCell(record)}</td>
|
<td>{renderProjectCell(record)}</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -195,10 +175,10 @@ export default function AttendanceShiftTable({
|
|||||||
title="Zobrazit polohu"
|
title="Zobrazit polohu"
|
||||||
aria-label="Zobrazit polohu"
|
aria-label="Zobrazit polohu"
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">{"\uD83D\uDCCD"}</span>
|
<span aria-hidden="true">{"📍"}</span>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
"\u2014"
|
"—"
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ReactNode } from "react";
|
import { useEffect, type ReactNode } from "react";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
|
|
||||||
interface ConfirmModalProps {
|
interface ConfirmModalProps {
|
||||||
@@ -91,6 +91,19 @@ export default function ConfirmModal({
|
|||||||
confirmVariant,
|
confirmVariant,
|
||||||
loading,
|
loading,
|
||||||
}: ConfirmModalProps) {
|
}: ConfirmModalProps) {
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) return;
|
||||||
|
|
||||||
|
function handleKeyDown(e: KeyboardEvent) {
|
||||||
|
if (e.key === "Escape" && !loading) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||||
|
}, [isOpen, loading, onClose]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
|
|||||||
118
src/admin/components/FormModal.tsx
Normal file
118
src/admin/components/FormModal.tsx
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import { useEffect, type ReactNode, type FormEvent } from "react";
|
||||||
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
|
import useModalLock from "../hooks/useModalLock";
|
||||||
|
|
||||||
|
export interface FormModalProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
onSubmit?: () => void; // called when user clicks primary button (only if provided)
|
||||||
|
title: string;
|
||||||
|
children: ReactNode; // modal body (form fields)
|
||||||
|
submitLabel?: string; // primary button text
|
||||||
|
cancelLabel?: string; // cancel button text
|
||||||
|
loading?: boolean;
|
||||||
|
hideFooter?: boolean; // for "view-only" modals
|
||||||
|
size?: "sm" | "md" | "lg"; // optional
|
||||||
|
closeOnBackdrop?: boolean; // default true
|
||||||
|
closeOnEsc?: boolean; // default true
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function FormModal({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
onSubmit,
|
||||||
|
title,
|
||||||
|
children,
|
||||||
|
submitLabel = "Uložit",
|
||||||
|
cancelLabel = "Zrušit",
|
||||||
|
loading = false,
|
||||||
|
hideFooter = false,
|
||||||
|
size = "md",
|
||||||
|
closeOnBackdrop = true,
|
||||||
|
closeOnEsc = true,
|
||||||
|
}: FormModalProps) {
|
||||||
|
useModalLock(isOpen);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen || !closeOnEsc) return;
|
||||||
|
|
||||||
|
function handleKeyDown(e: KeyboardEvent) {
|
||||||
|
if (e.key === "Escape" && !loading) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleKeyDown);
|
||||||
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||||
|
}, [isOpen, closeOnEsc, loading, onClose]);
|
||||||
|
|
||||||
|
const handleBackdropClick = () => {
|
||||||
|
if (closeOnBackdrop && !loading) onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePrimaryClick = (e: FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (onSubmit) onSubmit();
|
||||||
|
};
|
||||||
|
|
||||||
|
const titleId = "form-modal-title";
|
||||||
|
const modalClassName =
|
||||||
|
size === "lg" ? "admin-modal admin-modal-lg" : "admin-modal";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatePresence>
|
||||||
|
{isOpen && (
|
||||||
|
<motion.div
|
||||||
|
className="admin-modal-overlay"
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
exit={{ opacity: 0 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
>
|
||||||
|
<div className="admin-modal-backdrop" onClick={handleBackdropClick} />
|
||||||
|
<motion.div
|
||||||
|
className={modalClassName}
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby={titleId}
|
||||||
|
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
>
|
||||||
|
<div className="admin-modal-header">
|
||||||
|
<h2 id={titleId} className="admin-modal-title">
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="admin-modal-body">{children}</div>
|
||||||
|
|
||||||
|
{!hideFooter && (
|
||||||
|
<div className="admin-modal-footer">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => !loading && onClose()}
|
||||||
|
className="admin-btn admin-btn-secondary"
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{cancelLabel}
|
||||||
|
</button>
|
||||||
|
{onSubmit && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handlePrimaryClick}
|
||||||
|
className="admin-btn admin-btn-primary"
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? "Zpracování..." : submitLabel}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,8 +4,6 @@ import { projectFilesOptions } from "../lib/queries/projects";
|
|||||||
import { useAlert } from "../context/AlertContext";
|
import { useAlert } from "../context/AlertContext";
|
||||||
import ConfirmModal from "./ConfirmModal";
|
import ConfirmModal from "./ConfirmModal";
|
||||||
import apiFetch from "../utils/api";
|
import apiFetch from "../utils/api";
|
||||||
import { Skeleton } from "boneyard-js/react";
|
|
||||||
import ProjectFileManagerFixture from "../fixtures/ProjectFileManagerFixture";
|
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
@@ -472,13 +470,9 @@ export default function ProjectFileManager({
|
|||||||
|
|
||||||
if (filesLoading && items.length === 0 && !errorMessage) {
|
if (filesLoading && items.length === 0 && !errorMessage) {
|
||||||
return (
|
return (
|
||||||
<Skeleton
|
<div className="admin-loading">
|
||||||
name="project-file-manager"
|
<div className="admin-spinner" />
|
||||||
loading={filesLoading && items.length === 0}
|
</div>
|
||||||
fixture={<ProjectFileManagerFixture />}
|
|
||||||
>
|
|
||||||
<div />
|
|
||||||
</Skeleton>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,15 @@ export interface ShiftFormModalProps {
|
|||||||
|
|
||||||
function ProjectTimeStatus({ form, projectLogs }: ProjectTimeStatusProps) {
|
function ProjectTimeStatus({ form, projectLogs }: ProjectTimeStatusProps) {
|
||||||
const totalWork = calcFormWorkMinutes(form);
|
const totalWork = calcFormWorkMinutes(form);
|
||||||
const totalProject = calcProjectMinutesTotal(projectLogs);
|
const totalProject = calcProjectMinutesTotal(
|
||||||
|
projectLogs.map((l) => ({
|
||||||
|
...l,
|
||||||
|
project_id:
|
||||||
|
l.project_id !== "" && l.project_id != null
|
||||||
|
? Number(l.project_id)
|
||||||
|
: undefined,
|
||||||
|
})),
|
||||||
|
);
|
||||||
const remaining = totalWork - totalProject;
|
const remaining = totalWork - totalProject;
|
||||||
const hasLogs = projectLogs.some((l) => l.project_id);
|
const hasLogs = projectLogs.some((l) => l.project_id);
|
||||||
|
|
||||||
@@ -329,7 +337,6 @@ export default function ShiftFormModal({
|
|||||||
<option value="work">Práce</option>
|
<option value="work">Práce</option>
|
||||||
<option value="vacation">Dovolená</option>
|
<option value="vacation">Dovolená</option>
|
||||||
<option value="sick">Nemoc</option>
|
<option value="sick">Nemoc</option>
|
||||||
<option value="holiday">Svátek</option>
|
|
||||||
<option value="unpaid">Neplacené volno</option>
|
<option value="unpaid">Neplacené volno</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -332,6 +332,201 @@ const menuSections: MenuSection[] = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "Sklad",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
path: "/warehouse",
|
||||||
|
label: "Přehled",
|
||||||
|
permission: "warehouse.view",
|
||||||
|
end: true,
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
|
||||||
|
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
|
||||||
|
<line x1="12" y1="22.08" x2="12" y2="12" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/warehouse/items",
|
||||||
|
label: "Položky",
|
||||||
|
permission: "warehouse.view",
|
||||||
|
matchPrefix: "/warehouse/items",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
|
||||||
|
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
|
||||||
|
<line x1="12" y1="22.08" x2="12" y2="12" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/warehouse/receipts",
|
||||||
|
label: "Příjmy",
|
||||||
|
permission: "warehouse.operate",
|
||||||
|
matchPrefix: "/warehouse/receipts",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<polyline points="17 11 12 6 7 11" />
|
||||||
|
<line x1="12" y1="6" x2="12" y2="18" />
|
||||||
|
<path d="M5 19h14" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/warehouse/issues",
|
||||||
|
label: "Výdeje",
|
||||||
|
permission: "warehouse.operate",
|
||||||
|
matchPrefix: "/warehouse/issues",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<polyline points="7 13 12 18 17 13" />
|
||||||
|
<line x1="12" y1="18" x2="12" y2="6" />
|
||||||
|
<path d="M5 5h14" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/warehouse/reservations",
|
||||||
|
label: "Rezervace",
|
||||||
|
permission: "warehouse.operate",
|
||||||
|
matchPrefix: "/warehouse/reservations",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
||||||
|
<circle cx="9" cy="7" r="4" />
|
||||||
|
<line x1="19" y1="8" x2="19" y2="14" />
|
||||||
|
<line x1="22" y1="11" x2="16" y2="11" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/warehouse/inventory",
|
||||||
|
label: "Inventura",
|
||||||
|
permission: "warehouse.inventory",
|
||||||
|
matchPrefix: "/warehouse/inventory",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<path d="M9 11l3 3L22 4" />
|
||||||
|
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/warehouse/reports",
|
||||||
|
label: "Reporty",
|
||||||
|
permission: "warehouse.view",
|
||||||
|
end: true,
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<line x1="18" y1="20" x2="18" y2="10" />
|
||||||
|
<line x1="12" y1="20" x2="12" y2="4" />
|
||||||
|
<line x1="6" y1="20" x2="6" y2="14" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/warehouse/categories",
|
||||||
|
label: "Kategorie",
|
||||||
|
permission: "warehouse.manage",
|
||||||
|
matchPrefix: "/warehouse/categories",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<line x1="4" y1="21" x2="4" y2="14" />
|
||||||
|
<line x1="4" y1="10" x2="4" y2="3" />
|
||||||
|
<line x1="12" y1="21" x2="12" y2="12" />
|
||||||
|
<line x1="12" y1="8" x2="12" y2="3" />
|
||||||
|
<line x1="20" y1="21" x2="20" y2="16" />
|
||||||
|
<line x1="20" y1="12" x2="20" y2="3" />
|
||||||
|
<line x1="1" y1="14" x2="7" y2="14" />
|
||||||
|
<line x1="9" y1="8" x2="15" y2="8" />
|
||||||
|
<line x1="17" y1="16" x2="23" y2="16" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/warehouse/locations",
|
||||||
|
label: "Lokace",
|
||||||
|
permission: "warehouse.manage",
|
||||||
|
matchPrefix: "/warehouse/locations",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<rect x="3" y="3" width="7" height="7" rx="1" />
|
||||||
|
<rect x="14" y="3" width="7" height="7" rx="1" />
|
||||||
|
<rect x="3" y="14" width="7" height="7" rx="1" />
|
||||||
|
<rect x="14" y="14" width="7" height="7" rx="1" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/warehouse/suppliers",
|
||||||
|
label: "Dodavatelé",
|
||||||
|
permission: "warehouse.manage",
|
||||||
|
matchPrefix: "/warehouse/suppliers",
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
||||||
|
<circle cx="9" cy="7" r="4" />
|
||||||
|
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
||||||
|
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "Systém",
|
label: "Systém",
|
||||||
items: [
|
items: [
|
||||||
@@ -360,6 +555,7 @@ const menuSections: MenuSection[] = [
|
|||||||
"settings.company",
|
"settings.company",
|
||||||
"settings.banking",
|
"settings.banking",
|
||||||
"settings.roles",
|
"settings.roles",
|
||||||
|
"settings.system",
|
||||||
"settings.templates",
|
"settings.templates",
|
||||||
],
|
],
|
||||||
icon: (
|
icon: (
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ import useModalLock from "../../hooks/useModalLock";
|
|||||||
import apiFetch from "../../utils/api";
|
import apiFetch from "../../utils/api";
|
||||||
import { formatSessionDate } from "../../utils/dashboardHelpers";
|
import { formatSessionDate } from "../../utils/dashboardHelpers";
|
||||||
import { sessionsOptions, type Session } from "../../lib/queries/dashboard";
|
import { sessionsOptions, type Session } from "../../lib/queries/dashboard";
|
||||||
import { Skeleton } from "boneyard-js/react";
|
|
||||||
import DashSessionsFixture from "../../fixtures/DashSessionsFixture";
|
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
@@ -154,11 +152,11 @@ export default function DashSessions() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="admin-card-body" style={{ padding: 0 }}>
|
<div className="admin-card-body" style={{ padding: 0 }}>
|
||||||
<Skeleton
|
{sessionsLoading ? (
|
||||||
name="dash-sessions"
|
<div className="admin-loading">
|
||||||
loading={sessionsLoading}
|
<div className="admin-spinner" />
|
||||||
fixture={<DashSessionsFixture />}
|
</div>
|
||||||
>
|
) : (
|
||||||
<>
|
<>
|
||||||
{sessions.length === 0 && (
|
{sessions.length === 0 && (
|
||||||
<div
|
<div
|
||||||
@@ -231,7 +229,7 @@ export default function DashSessions() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
</Skeleton>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
|
|||||||
74
src/admin/components/warehouse/BatchPicker.tsx
Normal file
74
src/admin/components/warehouse/BatchPicker.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { jsonQuery } from "../../lib/apiAdapter";
|
||||||
|
|
||||||
|
interface Batch {
|
||||||
|
id: number;
|
||||||
|
quantity: number;
|
||||||
|
unit_price: number;
|
||||||
|
received_at: string;
|
||||||
|
is_consumed: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BatchesResponse {
|
||||||
|
batches: Batch[];
|
||||||
|
total_stock: number;
|
||||||
|
reserved_quantity: number;
|
||||||
|
available_quantity: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BatchPickerProps {
|
||||||
|
itemId: number | null;
|
||||||
|
value: number | null;
|
||||||
|
onChange: (batchId: number, unitPrice: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function BatchPicker({
|
||||||
|
itemId,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: BatchPickerProps) {
|
||||||
|
const { data: response } = useQuery({
|
||||||
|
queryKey: ["warehouse", "batches", itemId],
|
||||||
|
queryFn: () =>
|
||||||
|
jsonQuery<BatchesResponse>(
|
||||||
|
`/api/admin/warehouse/items/${itemId}/batches`,
|
||||||
|
),
|
||||||
|
enabled: !!itemId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const batches = response?.batches ?? [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{response && response.reserved_quantity > 0 && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: "0.75rem",
|
||||||
|
color: "var(--text-tertiary)",
|
||||||
|
marginBottom: "0.25rem",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Dostupné: {response.available_quantity} ks (z toho rezervováno:{" "}
|
||||||
|
{response.reserved_quantity} ks)
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<select
|
||||||
|
className="admin-form-select"
|
||||||
|
value={value ?? ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
const batchId = Number(e.target.value);
|
||||||
|
const batch = batches.find((b) => b.id === batchId);
|
||||||
|
if (batch) onChange(batch.id, Number(batch.unit_price));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="">-- auto FIFO --</option>
|
||||||
|
{batches.map((b) => (
|
||||||
|
<option key={b.id} value={b.id}>
|
||||||
|
{new Date(b.received_at).toLocaleDateString("cs-CZ")} | {b.quantity}{" "}
|
||||||
|
ks | {Number(b.unit_price).toFixed(2)} Kč
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
228
src/admin/components/warehouse/ItemPicker.tsx
Normal file
228
src/admin/components/warehouse/ItemPicker.tsx
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
import { useState, useRef, useEffect, useCallback, useId } from "react";
|
||||||
|
import { createPortal } from "react-dom";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { warehouseItemListOptions } from "../../lib/queries/warehouse";
|
||||||
|
|
||||||
|
interface ItemPickerProps {
|
||||||
|
value: number | null;
|
||||||
|
onChange: (itemId: number) => void;
|
||||||
|
itemName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ItemPicker({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
itemName,
|
||||||
|
}: ItemPickerProps) {
|
||||||
|
const [search, setSearch] = useState(itemName ?? "");
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const { data } = useQuery(warehouseItemListOptions({ search, perPage: 20 }));
|
||||||
|
const items = data?.data ?? [];
|
||||||
|
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const activeIndexRef = useRef<number>(-1);
|
||||||
|
const listboxId = useId();
|
||||||
|
const [activeIndex, setActiveIndex] = useState<number>(-1);
|
||||||
|
|
||||||
|
const [dropdownStyle, setDropdownStyle] = useState<{
|
||||||
|
position: "fixed";
|
||||||
|
top: number;
|
||||||
|
left: number;
|
||||||
|
width: number;
|
||||||
|
zIndex: number;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
|
const updatePosition = useCallback(() => {
|
||||||
|
if (!containerRef.current) return;
|
||||||
|
const rect = containerRef.current.getBoundingClientRect();
|
||||||
|
setDropdownStyle({
|
||||||
|
position: "fixed",
|
||||||
|
top: rect.bottom + 2,
|
||||||
|
left: rect.left,
|
||||||
|
width: rect.width,
|
||||||
|
zIndex: 100,
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Reset active index when items change
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeIndex >= items.length) {
|
||||||
|
activeIndexRef.current = -1;
|
||||||
|
setActiveIndex(-1);
|
||||||
|
}
|
||||||
|
}, [items, activeIndex]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
updatePosition();
|
||||||
|
const onScroll = () => updatePosition();
|
||||||
|
const onClose = () => setOpen(false);
|
||||||
|
window.addEventListener("scroll", onScroll, true);
|
||||||
|
window.addEventListener("resize", onClose);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("scroll", onScroll, true);
|
||||||
|
window.removeEventListener("resize", onClose);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
setDropdownStyle(null);
|
||||||
|
activeIndexRef.current = -1;
|
||||||
|
setActiveIndex(-1);
|
||||||
|
}
|
||||||
|
}, [open, updatePosition]);
|
||||||
|
|
||||||
|
// Close on click-outside via mousedown on document
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) return;
|
||||||
|
const onDocMouseDown = (e: MouseEvent) => {
|
||||||
|
const target = e.target;
|
||||||
|
if (containerRef.current && target instanceof Node) {
|
||||||
|
if (!containerRef.current.contains(target)) {
|
||||||
|
// Don't close if click landed on an option in the portal
|
||||||
|
const listEl = document.getElementById(listboxId);
|
||||||
|
if (listEl && listEl.contains(target)) return;
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.addEventListener("mousedown", onDocMouseDown);
|
||||||
|
return () => document.removeEventListener("mousedown", onDocMouseDown);
|
||||||
|
}, [open, listboxId]);
|
||||||
|
|
||||||
|
const handleSelect = (itemId: number, name: string) => {
|
||||||
|
onChange(itemId);
|
||||||
|
setOpen(false);
|
||||||
|
setSearch(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setActive = (index: number) => {
|
||||||
|
activeIndexRef.current = index;
|
||||||
|
setActiveIndex(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === "ArrowDown") {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!open) {
|
||||||
|
setOpen(true);
|
||||||
|
if (items.length > 0) setActive(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const next =
|
||||||
|
activeIndexRef.current < items.length - 1
|
||||||
|
? activeIndexRef.current + 1
|
||||||
|
: 0;
|
||||||
|
setActive(next);
|
||||||
|
} else if (e.key === "ArrowUp") {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!open) {
|
||||||
|
setOpen(true);
|
||||||
|
if (items.length > 0) setActive(items.length - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const prev =
|
||||||
|
activeIndexRef.current > 0
|
||||||
|
? activeIndexRef.current - 1
|
||||||
|
: items.length - 1;
|
||||||
|
setActive(prev);
|
||||||
|
} else if (e.key === "Enter") {
|
||||||
|
if (
|
||||||
|
open &&
|
||||||
|
activeIndexRef.current >= 0 &&
|
||||||
|
activeIndexRef.current < items.length
|
||||||
|
) {
|
||||||
|
e.preventDefault();
|
||||||
|
const item = items[activeIndexRef.current];
|
||||||
|
handleSelect(item.id, item.name);
|
||||||
|
}
|
||||||
|
} else if (e.key === "Escape") {
|
||||||
|
if (open) {
|
||||||
|
e.preventDefault();
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
} else if (e.key === "Home") {
|
||||||
|
if (open && items.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
setActive(0);
|
||||||
|
}
|
||||||
|
} else if (e.key === "End") {
|
||||||
|
if (open && items.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
setActive(items.length - 1);
|
||||||
|
}
|
||||||
|
} else if (e.key === "Tab") {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeId =
|
||||||
|
activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
style={{ position: "relative" }}
|
||||||
|
role="combobox"
|
||||||
|
aria-haspopup="listbox"
|
||||||
|
aria-expanded={open}
|
||||||
|
aria-owns={listboxId}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="admin-form-input"
|
||||||
|
placeholder="Hledat položku..."
|
||||||
|
value={search}
|
||||||
|
role="searchbox"
|
||||||
|
aria-autocomplete="list"
|
||||||
|
aria-controls={listboxId}
|
||||||
|
aria-activedescendant={activeId}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSearch(e.target.value);
|
||||||
|
setOpen(true);
|
||||||
|
}}
|
||||||
|
onFocus={() => {
|
||||||
|
setOpen(true);
|
||||||
|
updatePosition();
|
||||||
|
}}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
{open &&
|
||||||
|
items.length > 0 &&
|
||||||
|
dropdownStyle &&
|
||||||
|
createPortal(
|
||||||
|
<ul
|
||||||
|
id={listboxId}
|
||||||
|
role="listbox"
|
||||||
|
className="admin-item-picker-list"
|
||||||
|
style={dropdownStyle}
|
||||||
|
>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<li
|
||||||
|
key={item.id}
|
||||||
|
id={`${listboxId}-option-${index}`}
|
||||||
|
role="option"
|
||||||
|
aria-selected={value === item.id}
|
||||||
|
className={`admin-item-picker-item ${activeIndex === index ? "active" : ""}`}
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleSelect(item.id, item.name);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="admin-item-picker-name">{item.name}</span>
|
||||||
|
{item.item_number && (
|
||||||
|
<span className="admin-item-picker-number">
|
||||||
|
{item.item_number}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{item.available_quantity !== undefined && (
|
||||||
|
<span className="admin-item-picker-qty">
|
||||||
|
{item.available_quantity} {item.unit}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>,
|
||||||
|
document.body,
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
31
src/admin/components/warehouse/LocationSelect.tsx
Normal file
31
src/admin/components/warehouse/LocationSelect.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import {
|
||||||
|
warehouseLocationListOptions,
|
||||||
|
type WarehouseLocation,
|
||||||
|
} from "../../lib/queries/warehouse";
|
||||||
|
|
||||||
|
interface LocationSelectProps {
|
||||||
|
value: number | null;
|
||||||
|
onChange: (locationId: number | null) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LocationSelect({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: LocationSelectProps) {
|
||||||
|
const { data: locations } = useQuery(warehouseLocationListOptions());
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
className="admin-form-select"
|
||||||
|
value={value ?? ""}
|
||||||
|
onChange={(e) => onChange(e.target.value ? Number(e.target.value) : null)}
|
||||||
|
>
|
||||||
|
<option value="">-- bez lokace --</option>
|
||||||
|
{locations?.map((loc: WarehouseLocation) => (
|
||||||
|
<option key={loc.id} value={loc.id}>
|
||||||
|
{loc.code} - {loc.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
||||||
54
src/admin/components/warehouse/ReservationPicker.tsx
Normal file
54
src/admin/components/warehouse/ReservationPicker.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { warehouseReservationListOptions } from "../../lib/queries/warehouse";
|
||||||
|
|
||||||
|
interface ReservationPickerProps {
|
||||||
|
itemId: number | null;
|
||||||
|
projectId: number | null;
|
||||||
|
value: number | null;
|
||||||
|
onChange: (reservationId: number | null, remainingQty: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ReservationPicker({
|
||||||
|
itemId,
|
||||||
|
projectId,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: ReservationPickerProps) {
|
||||||
|
const { data: result } = useQuery(
|
||||||
|
warehouseReservationListOptions({
|
||||||
|
item_id: itemId ?? undefined,
|
||||||
|
project_id: projectId ?? undefined,
|
||||||
|
status: "ACTIVE",
|
||||||
|
perPage: 100,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const reservations = result?.data ?? [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
className="admin-form-select"
|
||||||
|
value={value ?? ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = e.target.value;
|
||||||
|
if (!val) {
|
||||||
|
onChange(null, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const reservationId = Number(val);
|
||||||
|
const reservation = reservations.find((r) => r.id === reservationId);
|
||||||
|
if (reservation) {
|
||||||
|
onChange(reservationId, Number(reservation.remaining_qty));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="">— žádná rezervace —</option>
|
||||||
|
{reservations.map((r) => (
|
||||||
|
<option key={r.id} value={r.id}>
|
||||||
|
R{r.id} — {r.project?.name ?? `Projekt #${r.project_id}`} — zbývá:{" "}
|
||||||
|
{Number(r.remaining_qty)} {r.item?.unit ?? "ks"}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
||||||
33
src/admin/components/warehouse/SupplierSelect.tsx
Normal file
33
src/admin/components/warehouse/SupplierSelect.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import {
|
||||||
|
warehouseSupplierListOptions,
|
||||||
|
type WarehouseSupplier,
|
||||||
|
} from "../../lib/queries/warehouse";
|
||||||
|
|
||||||
|
interface SupplierSelectProps {
|
||||||
|
value: number | null;
|
||||||
|
onChange: (supplierId: number | null) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SupplierSelect({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: SupplierSelectProps) {
|
||||||
|
const { data } = useQuery(warehouseSupplierListOptions({ perPage: 100 }));
|
||||||
|
const suppliers = data?.data ?? [];
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
className="admin-form-select"
|
||||||
|
value={value ?? ""}
|
||||||
|
onChange={(e) => onChange(e.target.value ? Number(e.target.value) : null)}
|
||||||
|
>
|
||||||
|
<option value="">-- bez dodavatele --</option>
|
||||||
|
{suppliers.map((s: WarehouseSupplier) => (
|
||||||
|
<option key={s.id} value={s.id}>
|
||||||
|
{s.name}
|
||||||
|
{s.ico ? ` (${s.ico})` : ""}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
);
|
||||||
|
}
|
||||||
249
src/admin/components/warehouse/WarehouseMovementTable.tsx
Normal file
249
src/admin/components/warehouse/WarehouseMovementTable.tsx
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
import { ReactNode, useId, useRef } from "react";
|
||||||
|
import ItemPicker from "./ItemPicker";
|
||||||
|
import LocationSelect from "./LocationSelect";
|
||||||
|
import BatchPicker from "./BatchPicker";
|
||||||
|
|
||||||
|
export interface MovementItem {
|
||||||
|
key: string;
|
||||||
|
item_id: number | null;
|
||||||
|
item_name?: string;
|
||||||
|
quantity: number;
|
||||||
|
unit_price: number;
|
||||||
|
location_id: number | null;
|
||||||
|
batch_id: number | null;
|
||||||
|
reservation_id: number | null;
|
||||||
|
notes: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type MovementRowRenderer<T> = (
|
||||||
|
item: T,
|
||||||
|
index: number,
|
||||||
|
updateItem: (field: string, value: unknown) => void,
|
||||||
|
removeItem: () => void,
|
||||||
|
) => ReactNode;
|
||||||
|
|
||||||
|
interface WarehouseMovementTableProps<T extends { key: string }> {
|
||||||
|
items: T[];
|
||||||
|
onChange: (items: T[]) => void;
|
||||||
|
mode: "receipt" | "issue" | "inventory";
|
||||||
|
defaultItem?: () => Omit<T, "key">;
|
||||||
|
renderRow?: MovementRowRenderer<T>;
|
||||||
|
renderHeader?: () => ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDecimal(raw: string): number {
|
||||||
|
// Strip leading minus if you want negative support; here we want only positive
|
||||||
|
const cleaned = raw.replace(/[eE+\-]/g, "");
|
||||||
|
const n = Number(cleaned);
|
||||||
|
return Number.isFinite(n) ? n : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const builtInDefaultMovementItem = (): Omit<MovementItem, "key"> => ({
|
||||||
|
item_id: null,
|
||||||
|
quantity: 0,
|
||||||
|
unit_price: 0,
|
||||||
|
location_id: null,
|
||||||
|
batch_id: null,
|
||||||
|
reservation_id: null,
|
||||||
|
notes: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function WarehouseMovementTable<T extends { key: string }>({
|
||||||
|
items,
|
||||||
|
onChange,
|
||||||
|
mode,
|
||||||
|
defaultItem,
|
||||||
|
renderRow,
|
||||||
|
renderHeader,
|
||||||
|
}: WarehouseMovementTableProps<T>) {
|
||||||
|
const baseId = useId();
|
||||||
|
const counterRef = useRef(0);
|
||||||
|
const nextKey = () => `${baseId}-item-${counterRef.current++}`;
|
||||||
|
|
||||||
|
const addItem = () => {
|
||||||
|
const factory =
|
||||||
|
defaultItem ??
|
||||||
|
(builtInDefaultMovementItem as unknown as () => Omit<T, "key">);
|
||||||
|
onChange([...items, { ...(factory() as object), key: nextKey() } as T]);
|
||||||
|
};
|
||||||
|
const removeItem = (index: number) => {
|
||||||
|
onChange(items.filter((_, i) => i !== index));
|
||||||
|
};
|
||||||
|
const updateItem = (index: number, field: string, value: unknown) => {
|
||||||
|
const updated = [...items];
|
||||||
|
updated[index] = { ...updated[index], [field]: value };
|
||||||
|
onChange(updated);
|
||||||
|
};
|
||||||
|
|
||||||
|
// If a custom row renderer is supplied, the caller is fully in charge of
|
||||||
|
// the row layout (and typically the column headers too — see inventory mode).
|
||||||
|
if (renderRow) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="admin-warehouse-movement-table">
|
||||||
|
<table className="admin-table">
|
||||||
|
{renderHeader && (
|
||||||
|
<thead>
|
||||||
|
<tr>{renderHeader()}</tr>
|
||||||
|
</thead>
|
||||||
|
)}
|
||||||
|
<tbody>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<tr key={item.key}>
|
||||||
|
{renderRow(
|
||||||
|
item,
|
||||||
|
index,
|
||||||
|
(field, value) => updateItem(index, field, value),
|
||||||
|
() => removeItem(index),
|
||||||
|
)}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="admin-btn admin-btn-secondary"
|
||||||
|
onClick={addItem}
|
||||||
|
>
|
||||||
|
+ Přidat řádek
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default rendering: only valid for "receipt" | "issue" — narrow at runtime.
|
||||||
|
const movementItems = items as unknown as MovementItem[];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className="admin-warehouse-movement-table">
|
||||||
|
<table className="admin-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="admin-warehouse-col-item">Položka</th>
|
||||||
|
{mode === "issue" && (
|
||||||
|
<th className="admin-warehouse-col-batch">Šarže</th>
|
||||||
|
)}
|
||||||
|
<th className="admin-warehouse-col-qty">Množství</th>
|
||||||
|
<th className="admin-warehouse-col-price">Cena/ks</th>
|
||||||
|
<th className="admin-warehouse-col-location">Lokace</th>
|
||||||
|
<th className="admin-warehouse-col-notes">Poznámka</th>
|
||||||
|
<th>Akce</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{movementItems.map((item, index) => (
|
||||||
|
<tr key={item.key}>
|
||||||
|
<td className="admin-warehouse-col-item">
|
||||||
|
<ItemPicker
|
||||||
|
value={item.item_id}
|
||||||
|
itemName={item.item_name}
|
||||||
|
onChange={(id) => updateItem(index, "item_id", id)}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
{mode === "issue" && (
|
||||||
|
<td className="admin-warehouse-col-batch">
|
||||||
|
<BatchPicker
|
||||||
|
itemId={item.item_id}
|
||||||
|
value={item.batch_id}
|
||||||
|
onChange={(batchId, unitPrice) => {
|
||||||
|
updateItem(index, "batch_id", batchId);
|
||||||
|
updateItem(index, "unit_price", unitPrice);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
|
<td className="admin-warehouse-col-qty">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
className="admin-form-input"
|
||||||
|
value={item.quantity || ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateItem(
|
||||||
|
index,
|
||||||
|
"quantity",
|
||||||
|
parseDecimal(e.target.value),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
min="0"
|
||||||
|
step="0.001"
|
||||||
|
inputMode="decimal"
|
||||||
|
pattern="[0-9]+([\.,][0-9]+)?"
|
||||||
|
aria-label={`Množství, řádek ${index + 1}`}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className="admin-warehouse-col-price">
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
className="admin-form-input"
|
||||||
|
value={item.unit_price || ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateItem(
|
||||||
|
index,
|
||||||
|
"unit_price",
|
||||||
|
parseDecimal(e.target.value),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
min="0"
|
||||||
|
step="0.01"
|
||||||
|
disabled={mode === "issue"}
|
||||||
|
inputMode="decimal"
|
||||||
|
pattern="[0-9]+([\.,][0-9]+)?"
|
||||||
|
aria-label={`Cena za kus, řádek ${index + 1}`}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className="admin-warehouse-col-location">
|
||||||
|
<LocationSelect
|
||||||
|
value={item.location_id}
|
||||||
|
onChange={(id) => updateItem(index, "location_id", id)}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className="admin-warehouse-col-notes">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="admin-form-input"
|
||||||
|
value={item.notes ?? ""}
|
||||||
|
onChange={(e) => updateItem(index, "notes", e.target.value)}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div className="admin-table-actions">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="admin-btn-icon danger"
|
||||||
|
onClick={() => removeItem(index)}
|
||||||
|
title="Odebrat řádek"
|
||||||
|
aria-label="Odebrat řádek"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18" />
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="admin-btn admin-btn-secondary"
|
||||||
|
onClick={addItem}
|
||||||
|
>
|
||||||
|
+ Přidat řádek
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
export default function AttendanceAdminFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Správa docházky</h1>
|
|
||||||
</div>
|
|
||||||
<div className="admin-page-actions">
|
|
||||||
<button className="admin-btn admin-btn-secondary">
|
|
||||||
Vyplnit měsíc
|
|
||||||
</button>
|
|
||||||
<button className="admin-btn admin-btn-primary">Přidat záznam</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card mb-6">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-form-row">
|
|
||||||
<label className="admin-form-label">Měsíc</label>
|
|
||||||
<select className="admin-form-select" />
|
|
||||||
<label className="admin-form-label">Zaměstnanec</label>
|
|
||||||
<select className="admin-form-select" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-grid admin-grid-3">
|
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
|
||||||
<div key={i} className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="flex-row gap-2 mb-2">
|
|
||||||
<span style={{ fontWeight: 600 }}>Jan Novák</span>
|
|
||||||
<span className="attendance-working-badge finished">
|
|
||||||
✗
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="admin-stat-value">8:00</div>
|
|
||||||
<div className="admin-stat-label">odpracováno</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th>Příchod</th>
|
|
||||||
<th>Odchod</th>
|
|
||||||
<th>Hodiny</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 4 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>1. 4. 2025</td>
|
|
||||||
<td className="admin-mono">08:00</td>
|
|
||||||
<td className="admin-mono">16:00</td>
|
|
||||||
<td className="admin-mono">8:00</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,104 +0,0 @@
|
|||||||
export default function AttendanceBalancesFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Správa bilancí</h1>
|
|
||||||
</div>
|
|
||||||
<div className="admin-page-actions">
|
|
||||||
<select className="admin-form-select" style={{ minWidth: 100 }}>
|
|
||||||
<option>2025</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Zaměstnanec</th>
|
|
||||||
<th>Nárok (h)</th>
|
|
||||||
<th>Čerpáno (h)</th>
|
|
||||||
<th>Zbývá (h)</th>
|
|
||||||
<th>Nemoc (h)</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 4 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="fw-500">Jan Novák</td>
|
|
||||||
<td className="admin-mono">160</td>
|
|
||||||
<td className="admin-mono">40.0</td>
|
|
||||||
<td className="admin-mono">120.0</td>
|
|
||||||
<td className="admin-mono">8.0</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon" title="Upravit">
|
|
||||||
✎
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-6">
|
|
||||||
<h2 className="admin-page-title mb-4" style={{ fontSize: "1.25rem" }}>
|
|
||||||
Měsíční přehled fondu 2025
|
|
||||||
</h2>
|
|
||||||
<div className="admin-grid admin-grid-3">
|
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
|
||||||
<div key={i} className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 style={{ fontWeight: 600, fontSize: "1rem", margin: 0 }}>
|
|
||||||
Duben
|
|
||||||
</h3>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: "0.375rem",
|
|
||||||
marginTop: "0.5rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
fontSize: 12,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>Jan Novák</span>
|
|
||||||
<span className="text-secondary">8h</span>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: 3,
|
|
||||||
background: "var(--bg-tertiary)",
|
|
||||||
borderRadius: 2,
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: "100%",
|
|
||||||
width: "75%",
|
|
||||||
background: "var(--gradient)",
|
|
||||||
borderRadius: 2,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
export default function AttendanceCreateFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<h1 className="admin-page-title">Zapsat docházku</h1>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card" style={{ maxWidth: 600 }}>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<FormField label="Uživatel">
|
|
||||||
<select className="admin-form-select">
|
|
||||||
<option>Jan Novák</option>
|
|
||||||
</select>
|
|
||||||
</FormField>
|
|
||||||
<FormField label="Datum">
|
|
||||||
<input type="date" className="admin-form-input" />
|
|
||||||
</FormField>
|
|
||||||
<FormField label="Příchod">
|
|
||||||
<input type="time" className="admin-form-input" />
|
|
||||||
</FormField>
|
|
||||||
<FormField label="Odchod">
|
|
||||||
<input type="time" className="admin-form-input" />
|
|
||||||
</FormField>
|
|
||||||
<button className="admin-btn admin-btn-primary">Uložit</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function FormField({
|
|
||||||
label,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
label: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">{label}</label>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
export default function AttendanceFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Docházka</h1>
|
|
||||||
<p className="admin-page-subtitle">pondělí 28. dubna 2025</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="attendance-layout">
|
|
||||||
<div className="attendance-main">
|
|
||||||
<div className="attendance-clock-card">
|
|
||||||
<div className="attendance-clock-header">
|
|
||||||
<div className="attendance-clock-status">
|
|
||||||
<span className="attendance-status-dot" />
|
|
||||||
<span>Nepracuji</span>
|
|
||||||
</div>
|
|
||||||
<div className="attendance-clock-time">08:30</div>
|
|
||||||
</div>
|
|
||||||
<div className="attendance-clock-actions">
|
|
||||||
<button className="admin-btn admin-btn-primary w-full">
|
|
||||||
Příchod
|
|
||||||
</button>
|
|
||||||
<button className="admin-btn admin-btn-secondary w-full">
|
|
||||||
Žádost o nepřítomnost
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="attendance-sidebar">
|
|
||||||
<div className="attendance-balance-card">
|
|
||||||
<h3 className="attendance-balance-title">Dovolená 2025</h3>
|
|
||||||
<div className="attendance-balance-value">
|
|
||||||
<span className="attendance-balance-number">12</span>
|
|
||||||
<span className="attendance-balance-unit">dnů</span>
|
|
||||||
</div>
|
|
||||||
<div className="attendance-balance-detail">
|
|
||||||
<span>Celkem: 160h</span>
|
|
||||||
<span>Čerpáno: 64h</span>
|
|
||||||
</div>
|
|
||||||
<div className="attendance-balance-bar">
|
|
||||||
<div
|
|
||||||
className="attendance-balance-progress"
|
|
||||||
style={{ width: "60%" }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-stat-card">
|
|
||||||
<div className="admin-stat-icon danger">
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<path d="M22 12h-4l-3 9L9 3l-3 9H2" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div className="admin-stat-content">
|
|
||||||
<span className="admin-stat-label">Nemoc 2025</span>
|
|
||||||
<span className="admin-stat-value">16h čerpáno</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="attendance-quick-links">
|
|
||||||
<h4 className="attendance-quick-title">Rychlé odkazy</h4>
|
|
||||||
<a className="attendance-quick-link">
|
|
||||||
<span>Moje žádosti</span>
|
|
||||||
</a>
|
|
||||||
<a className="attendance-quick-link">
|
|
||||||
<span>Historie docházky</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
export default function AttendanceHistoryFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Historie docházky</h1>
|
|
||||||
<p className="admin-page-subtitle">duben 2025</p>
|
|
||||||
</div>
|
|
||||||
<div className="admin-page-actions">
|
|
||||||
<button className="admin-btn admin-btn-secondary">Tisk</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card mb-6">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-form-row">
|
|
||||||
<label className="admin-form-label">Měsíc</label>
|
|
||||||
<input className="admin-form-input" readOnly value="04/2025" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card mb-6">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "1rem" }}>
|
|
||||||
<div className="admin-stat-icon info">
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
|
|
||||||
<line x1="16" y1="2" x2="16" y2="6" />
|
|
||||||
<line x1="8" y1="2" x2="8" y2="6" />
|
|
||||||
<line x1="3" y1="10" x2="21" y2="10" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div style={{ flex: 1, minWidth: 200 }}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "baseline",
|
|
||||||
marginBottom: "0.375rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span style={{ fontWeight: 600 }}>Fond: 120h / 160h</span>
|
|
||||||
<span
|
|
||||||
className="text-secondary"
|
|
||||||
style={{ fontSize: "0.8125rem" }}
|
|
||||||
>
|
|
||||||
20 prac. dnů
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="attendance-balance-bar">
|
|
||||||
<div
|
|
||||||
className="attendance-balance-progress"
|
|
||||||
style={{ width: "75%" }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th>Typ</th>
|
|
||||||
<th>Příchod</th>
|
|
||||||
<th>Pauza</th>
|
|
||||||
<th>Odchod</th>
|
|
||||||
<th>Hodiny</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="admin-mono">1. 4. 2025</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-info">
|
|
||||||
Práce
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="admin-mono">08:00</td>
|
|
||||||
<td className="admin-mono">12:00 – 12:30</td>
|
|
||||||
<td className="admin-mono">16:30</td>
|
|
||||||
<td className="admin-mono">8:00</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
export default function AttendanceLocationFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.75rem" }}>
|
|
||||||
<svg
|
|
||||||
width="32"
|
|
||||||
height="32"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" />
|
|
||||||
<circle cx="12" cy="10" r="3" />
|
|
||||||
</svg>
|
|
||||||
<h1 className="admin-page-title">Lokace</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card" style={{ height: 300, marginBottom: "1rem" }}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
background: "var(--bg-secondary)",
|
|
||||||
height: "100%",
|
|
||||||
borderRadius: 8,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1rem" }}
|
|
||||||
>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 style={{ marginBottom: "0.5rem" }}>Poloha</h3>
|
|
||||||
<p>50.0755° N, 14.4378° E</p>
|
|
||||||
<p>Praha, Česká republika</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 style={{ marginBottom: "0.5rem" }}>Čas záznamu</h3>
|
|
||||||
<p>1. 1. 2024 08:00</p>
|
|
||||||
<p>Přesnost: 10 m</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
export default function AuditLogFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Audit log</h1>
|
|
||||||
<p className="admin-page-subtitle">Záznam změn v systému</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div
|
|
||||||
className="admin-search-bar mb-4"
|
|
||||||
style={{ display: "flex", gap: "0.5rem" }}
|
|
||||||
>
|
|
||||||
<input className="admin-form-input" placeholder="" />
|
|
||||||
<select className="admin-form-select" />
|
|
||||||
<select className="admin-form-select" />
|
|
||||||
</div>
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Čas</th>
|
|
||||||
<th>Uživatel</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
<th>Entita</th>
|
|
||||||
<th>Detail</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="admin-mono">1. 1. 2024 10:00</td>
|
|
||||||
<td>admin</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-create">
|
|
||||||
Vytvoření
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>Faktura</td>
|
|
||||||
<td style={{ maxWidth: 300 }}>Nová faktura FV-2024-001</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
export default function CompanySettingsFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Nastavení firmy</h1>
|
|
||||||
<p className="admin-page-subtitle">Firemní údaje a bankovní účty</p>
|
|
||||||
</div>
|
|
||||||
<button className="admin-btn admin-btn-primary">
|
|
||||||
Uložit nastavení
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="admin-settings-grid">
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-header">
|
|
||||||
<h3 className="admin-card-title">Firemní údaje</h3>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-form">
|
|
||||||
<label className="admin-form-label">Název firmy</label>
|
|
||||||
<input
|
|
||||||
className="admin-form-input"
|
|
||||||
readOnly
|
|
||||||
value="BOHA s.r.o."
|
|
||||||
/>
|
|
||||||
<div className="admin-form-row">
|
|
||||||
<label className="admin-form-label">Ulice</label>
|
|
||||||
<input
|
|
||||||
className="admin-form-input"
|
|
||||||
readOnly
|
|
||||||
value="Hlavní 123"
|
|
||||||
/>
|
|
||||||
<label className="admin-form-label">Město</label>
|
|
||||||
<input className="admin-form-input" readOnly value="Praha" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-header">
|
|
||||||
<h3 className="admin-card-title">Bankovní účty</h3>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Název</th>
|
|
||||||
<th>Banka</th>
|
|
||||||
<th>Číslo účtu</th>
|
|
||||||
<th>Měna</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Hlavní účet</td>
|
|
||||||
<td>ČSOB</td>
|
|
||||||
<td className="admin-mono">123456/0300</td>
|
|
||||||
<td>CZK</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
export default function DashSessionsFixture() {
|
|
||||||
return (
|
|
||||||
<div className="admin-card">
|
|
||||||
<div
|
|
||||||
className="admin-card-header"
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
gap: "0.75rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<h2 className="admin-card-title">Přihlášená zařízení</h2>
|
|
||||||
<button className="admin-btn admin-btn-secondary admin-btn-sm">
|
|
||||||
Odhlásit ostatní
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card-body" style={{ padding: 0 }}>
|
|
||||||
<div className="dash-sessions-list">
|
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className={`dash-session-item${i === 0 ? " dash-session-item-current" : ""}`}
|
|
||||||
>
|
|
||||||
<div className="dash-session-icon">
|
|
||||||
<svg
|
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
|
|
||||||
<line x1="8" y1="21" x2="16" y2="21" />
|
|
||||||
<line x1="12" y1="17" x2="12" y2="21" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div className="dash-session-info">
|
|
||||||
<div className="dash-session-device">
|
|
||||||
Chrome na Windows
|
|
||||||
{i === 0 && (
|
|
||||||
<span
|
|
||||||
className="admin-badge admin-badge-success"
|
|
||||||
style={{ marginLeft: "0.5rem" }}
|
|
||||||
>
|
|
||||||
Aktuální
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="dash-session-meta">
|
|
||||||
<span>192.168.1.100</span>
|
|
||||||
<span className="dash-session-meta-separator">|</span>
|
|
||||||
<span>před 2 hodinami</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
export default function DashboardFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Dashboard</h1>
|
|
||||||
<p className="admin-page-subtitle">Přehled</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="dash-kpi-grid"
|
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "repeat(4, 1fr)",
|
|
||||||
gap: "1rem",
|
|
||||||
marginBottom: "1rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{["Nabídky", "Objednávky", "Faktury", "Projekty"].map((label, i) => (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className="dash-kpi-card"
|
|
||||||
style={{
|
|
||||||
padding: "1.25rem",
|
|
||||||
borderRadius: 10,
|
|
||||||
background: "var(--bg-secondary)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ fontSize: "0.875rem", marginBottom: "0.25rem" }}>
|
|
||||||
{label}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: "1.5rem", fontWeight: 600 }}>12</div>
|
|
||||||
<div style={{ fontSize: "0.75rem" }}>tento měsíc</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="dash-quick-actions"
|
|
||||||
style={{ display: "flex", gap: "0.5rem", marginBottom: "1rem" }}
|
|
||||||
>
|
|
||||||
{["Nová nabídka", "Nová faktura", "Zapsat docházku"].map((label, i) => (
|
|
||||||
<button
|
|
||||||
key={i}
|
|
||||||
className="admin-btn admin-btn-secondary"
|
|
||||||
style={{ flex: 1 }}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "2fr 1fr",
|
|
||||||
gap: "1rem",
|
|
||||||
marginBottom: "1rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="admin-card" style={{ minHeight: 320 }}>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 className="admin-card-title">Docházka dnes</h3>
|
|
||||||
<div style={{ height: 200 }} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
|
|
||||||
<div className="admin-card" style={{ minHeight: 150 }}>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 className="admin-card-title">Aktivita</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card" style={{ minHeight: 150 }}>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 className="admin-card-title">Profil</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1rem" }}
|
|
||||||
>
|
|
||||||
<div className="admin-card" style={{ minHeight: 200 }}>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 className="admin-card-title">Relace</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card" style={{ minHeight: 200 }}>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 className="admin-card-title">Poslední aktivity</h3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
export default function InvoiceDetailFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
|
|
||||||
<a href="/invoices" className="admin-btn-icon">
|
|
||||||
<svg
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<path d="M19 12H5M12 19l-7-7 7-7" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">FV-2024-001</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-page-actions">
|
|
||||||
<button className="admin-btn admin-btn-primary">Uložit</button>
|
|
||||||
<button className="admin-btn admin-btn-secondary">Zaplaceno</button>
|
|
||||||
<button className="admin-btn admin-btn-secondary">Smazat</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card" style={{ marginBottom: "1rem" }}>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div
|
|
||||||
className="admin-form-row"
|
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "1fr 1fr",
|
|
||||||
gap: "1rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Zákazník</label>
|
|
||||||
<div>Firma s.r.o.</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Stav</label>
|
|
||||||
<span className="admin-badge admin-badge-invoice-issued">
|
|
||||||
Vystavena
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Datum vystavení</label>
|
|
||||||
<div>1. 1. 2024</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Datum splatnosti</label>
|
|
||||||
<div>15. 1. 2024</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 className="admin-card-title">Položky</h3>
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Položka</th>
|
|
||||||
<th>Množství</th>
|
|
||||||
<th>Cena</th>
|
|
||||||
<th>Celkem</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>Služba {i + 1}</td>
|
|
||||||
<td>1</td>
|
|
||||||
<td>10 000 Kč</td>
|
|
||||||
<td>10 000 Kč</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
export default function InvoicesFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Faktury</h1>
|
|
||||||
<p className="admin-page-subtitle">15 faktur</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="dash-kpi-grid"
|
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "repeat(4, 1fr)",
|
|
||||||
gap: "1rem",
|
|
||||||
marginBottom: "1rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{["Vystaveno", "Zaplaceno", "Po splatnosti", "Celkem"].map(
|
|
||||||
(label, i) => (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className="dash-kpi-card"
|
|
||||||
style={{
|
|
||||||
padding: "1.25rem",
|
|
||||||
borderRadius: 10,
|
|
||||||
background: "var(--bg-secondary)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ fontSize: "0.875rem", marginBottom: "0.25rem" }}>
|
|
||||||
{label}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: "1.5rem", fontWeight: 600 }}>
|
|
||||||
{i * 5 + 3}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-search-bar mb-4">
|
|
||||||
<input className="admin-form-input" placeholder="" />
|
|
||||||
</div>
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Číslo</th>
|
|
||||||
<th>Zákazník</th>
|
|
||||||
<th>Stav</th>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th className="text-right">Částka</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="admin-mono">FV-2024-00{i + 1}</td>
|
|
||||||
<td>Firma s.r.o.</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-invoice-issued">
|
|
||||||
Vystaveno
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="admin-mono">1. 1. 2024</td>
|
|
||||||
<td className="admin-mono text-right">50 000 Kč</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">👁</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
export default function LeaveApprovalFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Schvalování dovolené</h1>
|
|
||||||
<p className="admin-page-subtitle">2 čekající</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Uživatel</th>
|
|
||||||
<th>Typ</th>
|
|
||||||
<th>Od</th>
|
|
||||||
<th>Do</th>
|
|
||||||
<th>Dní</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>Jan Novák</td>
|
|
||||||
<td>Dovolená</td>
|
|
||||||
<td className="admin-mono">1. 7. 2024</td>
|
|
||||||
<td className="admin-mono">5. 7. 2024</td>
|
|
||||||
<td>5</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn admin-btn-sm admin-btn-primary">
|
|
||||||
Schválit
|
|
||||||
</button>
|
|
||||||
<button className="admin-btn admin-btn-sm admin-btn-secondary">
|
|
||||||
Zamítnout
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
export default function LeaveRequestsFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Žádosti o dovolenou</h1>
|
|
||||||
<p className="admin-page-subtitle">3 žádosti</p>
|
|
||||||
</div>
|
|
||||||
<button className="admin-btn admin-btn-primary">+ Nová žádost</button>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Uživatel</th>
|
|
||||||
<th>Typ</th>
|
|
||||||
<th>Od</th>
|
|
||||||
<th>Do</th>
|
|
||||||
<th>Dní</th>
|
|
||||||
<th>Stav</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>Jan Novák</td>
|
|
||||||
<td>Dovolená</td>
|
|
||||||
<td className="admin-mono">1. 7. 2024</td>
|
|
||||||
<td className="admin-mono">5. 7. 2024</td>
|
|
||||||
<td>5</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-pending">
|
|
||||||
Čeká
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">Zrušit</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
export default function OfferDetailFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<button className="admin-btn-icon">←</button>
|
|
||||||
<h1 className="admin-page-title">NAB-2024-001</h1>
|
|
||||||
<button className="admin-btn admin-btn-primary">Uložit</button>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div
|
|
||||||
className="admin-form-row"
|
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "1fr 1fr",
|
|
||||||
gap: "1rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Číslo nabídky</label>
|
|
||||||
<div className="admin-form-input">NAB-2024-001</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Zákazník</label>
|
|
||||||
<div className="admin-form-input">Firma s.r.o.</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Datum</label>
|
|
||||||
<div className="admin-form-input">1. 1. 2024</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Platnost do</label>
|
|
||||||
<div className="admin-form-input">31. 1. 2024</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<table className="admin-table" style={{ marginTop: "1rem" }}>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Položka</th>
|
|
||||||
<th>Množství</th>
|
|
||||||
<th>Cena</th>
|
|
||||||
<th>Celkem</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>Položka {i + 1}</td>
|
|
||||||
<td>1</td>
|
|
||||||
<td>10 000 Kč</td>
|
|
||||||
<td>10 000 Kč</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
export default function OffersCustomersFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Zákazníci</h1>
|
|
||||||
<p className="admin-page-subtitle">8 zákazníků</p>
|
|
||||||
</div>
|
|
||||||
<button className="admin-btn admin-btn-primary">
|
|
||||||
+ Přidat zákazníka
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-search-bar mb-4">
|
|
||||||
<input className="admin-form-input" placeholder="" />
|
|
||||||
</div>
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Název</th>
|
|
||||||
<th>Město</th>
|
|
||||||
<th>IČO</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>Firma s.r.o.</td>
|
|
||||||
<td>Praha</td>
|
|
||||||
<td className="admin-mono">12345678</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">✎</button>
|
|
||||||
<button className="admin-btn-icon danger">🗑</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
export default function OffersFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Nabídky</h1>
|
|
||||||
<p className="admin-page-subtitle">12 nabídek</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-search-bar mb-4">
|
|
||||||
<input className="admin-form-input" placeholder="" />
|
|
||||||
</div>
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Číslo</th>
|
|
||||||
<th>Zákazník</th>
|
|
||||||
<th>Stav</th>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th className="text-right">Celkem</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="admin-mono">NAB-2024-00{i + 1}</td>
|
|
||||||
<td>Firma s.r.o.</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-offer-active">
|
|
||||||
Aktivní
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="admin-mono">1. 1. 2024</td>
|
|
||||||
<td className="admin-mono text-right fw-500">100 000 Kč</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">👁</button>
|
|
||||||
<button className="admin-btn-icon">✎</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
export default function OffersTemplatesFixture() {
|
|
||||||
return (
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-search-bar mb-4">
|
|
||||||
<input className="admin-form-input" placeholder="" />
|
|
||||||
</div>
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Název</th>
|
|
||||||
<th>Cena</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>Šablona {i + 1}</td>
|
|
||||||
<td className="admin-mono text-right">1 000 Kč</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">✎</button>
|
|
||||||
<button className="admin-btn-icon danger">🗑</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
export default function OrderDetailFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div className="admin-page-header-left">
|
|
||||||
<Link
|
|
||||||
to="/orders"
|
|
||||||
className="admin-btn-icon"
|
|
||||||
style={{ marginRight: "0.5rem" }}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<path d="M19 12H5M12 19l-7-7 7-7" />
|
|
||||||
</svg>
|
|
||||||
</Link>
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">OBJ-2024-001</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-page-actions">
|
|
||||||
<button className="admin-btn admin-btn-primary">Uložit</button>
|
|
||||||
<button className="admin-btn admin-btn-secondary">Stornovat</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card" style={{ marginBottom: "1rem" }}>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div
|
|
||||||
className="admin-form-row"
|
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "1fr 1fr",
|
|
||||||
gap: "1rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Zákazník</label>
|
|
||||||
<div>Firma s.r.o.</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Stav</label>
|
|
||||||
<span className="admin-badge admin-badge-order-realizace">
|
|
||||||
V realizaci
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Datum vytvoření</label>
|
|
||||||
<div>1. 1. 2024</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Celkem</label>
|
|
||||||
<div className="fw-500">50 000 Kč</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 className="admin-card-title">Položky</h3>
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Položka</th>
|
|
||||||
<th>Množství</th>
|
|
||||||
<th>Cena</th>
|
|
||||||
<th>Celkem</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>Položka {i + 1}</td>
|
|
||||||
<td>1</td>
|
|
||||||
<td>10 000 Kč</td>
|
|
||||||
<td>10 000 Kč</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
export default function OrdersFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Objednávky</h1>
|
|
||||||
<p className="admin-page-subtitle">8 objednávek</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-search-bar mb-4">
|
|
||||||
<input className="admin-form-input" placeholder="" />
|
|
||||||
</div>
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Číslo</th>
|
|
||||||
<th>Nabídka</th>
|
|
||||||
<th>Zákazník</th>
|
|
||||||
<th>Stav</th>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th className="text-right">Celkem</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="admin-mono">OBJ-2024-00{i + 1}</td>
|
|
||||||
<td>NAB-2024-00{i + 1}</td>
|
|
||||||
<td>Firma s.r.o.</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-order-realizace">
|
|
||||||
V realizaci
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="admin-mono">1. 1. 2024</td>
|
|
||||||
<td className="admin-mono text-right fw-500">50 000 Kč</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">👁</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
export default function ProjectDetailFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
|
|
||||||
<a href="/projects" className="admin-btn-icon">
|
|
||||||
<svg
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<path d="M19 12H5M12 19l-7-7 7-7" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">PRJ-001 Projekt Alpha</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-page-actions">
|
|
||||||
<button className="admin-btn admin-btn-primary">Uložit</button>
|
|
||||||
<button className="admin-btn admin-btn-secondary">Smazat</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card" style={{ marginBottom: "1rem" }}>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div
|
|
||||||
className="admin-form-row"
|
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "1fr 1fr",
|
|
||||||
gap: "1rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Název projektu</label>
|
|
||||||
<input
|
|
||||||
className="admin-form-input"
|
|
||||||
value="Projekt Alpha"
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Stav</label>
|
|
||||||
<select className="admin-form-select">
|
|
||||||
<option>Aktivní</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Začátek</label>
|
|
||||||
<input type="date" className="admin-form-input" readOnly />
|
|
||||||
</div>
|
|
||||||
<div className="admin-form-group">
|
|
||||||
<label className="admin-form-label">Konec</label>
|
|
||||||
<input type="date" className="admin-form-input" readOnly />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card" style={{ marginBottom: "1rem" }}>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 className="admin-card-title">Poznámky</h3>
|
|
||||||
<textarea className="admin-form-input" rows={4} readOnly />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
export default function ProjectFileManagerFixture() {
|
|
||||||
return (
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<h3 className="admin-card-title">Soubory</h3>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "0.5rem",
|
|
||||||
marginBottom: "0.75rem",
|
|
||||||
fontSize: "0.875rem",
|
|
||||||
color: "var(--text-secondary)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>Projekt</span>
|
|
||||||
<span>/</span>
|
|
||||||
<span>Dokumentace</span>
|
|
||||||
</div>
|
|
||||||
{Array.from({ length: 4 }, (_, i) => (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "0.5rem",
|
|
||||||
padding: "0.5rem 0",
|
|
||||||
borderBottom: i < 3 ? "1px solid var(--border-color)" : "none",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
>
|
|
||||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
|
||||||
<polyline points="14 2 14 8 20 8" />
|
|
||||||
</svg>
|
|
||||||
<span style={{ flex: 1 }}>dokument_{i + 1}.pdf</span>
|
|
||||||
<span
|
|
||||||
style={{ color: "var(--text-secondary)", fontSize: "0.8rem" }}
|
|
||||||
>
|
|
||||||
2.{i + 1} MB
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
export default function ProjectsFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Projekty</h1>
|
|
||||||
<p className="admin-page-subtitle">6 projektů</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-search-bar mb-4">
|
|
||||||
<input className="admin-form-input" placeholder="" />
|
|
||||||
</div>
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Číslo</th>
|
|
||||||
<th>Název</th>
|
|
||||||
<th>Zákazník</th>
|
|
||||||
<th>Stav</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="admin-mono">PRJ-2024-00{i + 1}</td>
|
|
||||||
<td>Projekt Alpha</td>
|
|
||||||
<td>Firma s.r.o.</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-project-active">
|
|
||||||
Aktivní
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">👁</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
export default function ReceivedInvoicesFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Přijaté faktury</h1>
|
|
||||||
<p className="admin-page-subtitle">8 faktur</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="dash-kpi-grid"
|
|
||||||
style={{
|
|
||||||
display: "grid",
|
|
||||||
gridTemplateColumns: "repeat(4, 1fr)",
|
|
||||||
gap: "1rem",
|
|
||||||
marginBottom: "1rem",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{["K úhradě", "Zaplaceno", "Po splatnosti", "Celkem"].map(
|
|
||||||
(label, i) => (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
className="dash-kpi-card"
|
|
||||||
style={{
|
|
||||||
padding: "1.25rem",
|
|
||||||
borderRadius: 10,
|
|
||||||
background: "var(--bg-secondary)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div style={{ fontSize: "0.875rem", marginBottom: "0.25rem" }}>
|
|
||||||
{label}
|
|
||||||
</div>
|
|
||||||
<div style={{ fontSize: "1.5rem", fontWeight: 600 }}>
|
|
||||||
{i * 3 + 2}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Číslo</th>
|
|
||||||
<th>Dodavatel</th>
|
|
||||||
<th>Částka</th>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th>Stav</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="admin-mono">PF-2024-00{i + 1}</td>
|
|
||||||
<td>Dodavatel s.r.o.</td>
|
|
||||||
<td className="admin-mono text-right">10 000 Kč</td>
|
|
||||||
<td className="admin-mono">1. 1. 2024</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-invoice-issued">
|
|
||||||
K úhradě
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">✎</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
export default function SettingsFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Nastavení</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-tab-bar">
|
|
||||||
<button className="admin-tab active">Role</button>
|
|
||||||
<button className="admin-tab">Systém</button>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Název role</th>
|
|
||||||
<th>Popis</th>
|
|
||||||
<th>Oprávnění</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 3 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="fw-500">admin</td>
|
|
||||||
<td>Správa celého systému</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-info">
|
|
||||||
všechna
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon" title="Upravit">
|
|
||||||
✎
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-header">
|
|
||||||
<h2 className="admin-card-title">Docházka</h2>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-form">
|
|
||||||
<div className="admin-form-row">
|
|
||||||
<label className="admin-form-label">
|
|
||||||
Limit pro přestávku (hodiny)
|
|
||||||
</label>
|
|
||||||
<input className="admin-form-input" readOnly value="6" />
|
|
||||||
<label className="admin-form-label">
|
|
||||||
Délka krátké přestávky (min)
|
|
||||||
</label>
|
|
||||||
<input className="admin-form-input" readOnly value="15" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
export default function TripsAdminFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Správa jízd</h1>
|
|
||||||
<p className="admin-page-subtitle">10 jízd</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th>Vozidlo</th>
|
|
||||||
<th>Uživatel</th>
|
|
||||||
<th>Km</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="admin-mono">1. 1. 2024</td>
|
|
||||||
<td>Škoda Octavia</td>
|
|
||||||
<td>Jan Novák</td>
|
|
||||||
<td className="admin-mono">200</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">✎</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
export default function TripsFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Kniha jízd</h1>
|
|
||||||
<p className="admin-page-subtitle">duben 2025</p>
|
|
||||||
</div>
|
|
||||||
<div className="admin-page-actions">
|
|
||||||
<button className="admin-btn admin-btn-primary">Přidat jízdu</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-grid admin-grid-4">
|
|
||||||
<div className="admin-stat-card info">
|
|
||||||
<div className="admin-stat-icon info">
|
|
||||||
<svg
|
|
||||||
width="22"
|
|
||||||
height="22"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<line x1="12" y1="20" x2="12" y2="10" />
|
|
||||||
<line x1="18" y1="20" x2="18" y2="4" />
|
|
||||||
<line x1="6" y1="20" x2="6" y2="16" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div className="admin-stat-content">
|
|
||||||
<span className="admin-stat-value">12</span>
|
|
||||||
<span className="admin-stat-label">Počet jízd</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-stat-card">
|
|
||||||
<div className="admin-stat-icon">
|
|
||||||
<svg
|
|
||||||
width="22"
|
|
||||||
height="22"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<path d="M22 12h-4l-3 9L9 3l-3 9H2" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div className="admin-stat-content">
|
|
||||||
<span className="admin-stat-value">1 240 km</span>
|
|
||||||
<span className="admin-stat-label">Celkem naježděno</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-stat-card success">
|
|
||||||
<div className="admin-stat-icon success">
|
|
||||||
<svg
|
|
||||||
width="22"
|
|
||||||
height="22"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<rect x="1" y="3" width="15" height="13" rx="2" ry="2" />
|
|
||||||
<path d="M16 8h2a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-1" />
|
|
||||||
<circle cx="5.5" cy="18" r="2" />
|
|
||||||
<circle cx="18.5" cy="18" r="2" />
|
|
||||||
<path d="M8 18h8" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div className="admin-stat-content">
|
|
||||||
<span className="admin-stat-value">980 km</span>
|
|
||||||
<span className="admin-stat-label">Služební</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-stat-card warning">
|
|
||||||
<div className="admin-stat-icon warning">
|
|
||||||
<svg
|
|
||||||
width="22"
|
|
||||||
height="22"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
|
|
||||||
<polyline points="9 22 9 12 15 12 15 22" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div className="admin-stat-content">
|
|
||||||
<span className="admin-stat-value">260 km</span>
|
|
||||||
<span className="admin-stat-label">Soukromé</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card mt-6">
|
|
||||||
<div className="admin-card-header flex-between">
|
|
||||||
<h2 className="admin-card-title">Poslední jízdy</h2>
|
|
||||||
<a className="admin-btn admin-btn-secondary admin-btn-sm">
|
|
||||||
Zobrazit historii
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th>Vozidlo</th>
|
|
||||||
<th>Trasa</th>
|
|
||||||
<th>Vzdálenost</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 4 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="admin-mono">1. 4. 2025</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge">1A2 3456</span>
|
|
||||||
</td>
|
|
||||||
<td>Praha → Brno</td>
|
|
||||||
<td className="admin-mono">
|
|
||||||
<strong>200 km</strong>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
export default function TripsHistoryFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Historie jízd</h1>
|
|
||||||
<p className="admin-page-subtitle">10 jízd</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Datum</th>
|
|
||||||
<th>Vozidlo</th>
|
|
||||||
<th>Uživatel</th>
|
|
||||||
<th>Trasa</th>
|
|
||||||
<th>Km</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td className="admin-mono">1. 1. 2024</td>
|
|
||||||
<td>Škoda Octavia</td>
|
|
||||||
<td>Jan Novák</td>
|
|
||||||
<td>Praha → Brno</td>
|
|
||||||
<td className="admin-mono">200</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
export default function UsersFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<div>
|
|
||||||
<h1 className="admin-page-title">Uživatelé</h1>
|
|
||||||
<p className="admin-page-subtitle">5 uživatelů</p>
|
|
||||||
</div>
|
|
||||||
<button className="admin-btn admin-btn-primary">
|
|
||||||
+ Přidat uživatele
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-search-bar mb-4">
|
|
||||||
<input className="admin-form-input" placeholder="" />
|
|
||||||
</div>
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Uživatel</th>
|
|
||||||
<th>E-mail</th>
|
|
||||||
<th>Role</th>
|
|
||||||
<th>Stav</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-user">
|
|
||||||
<div className="admin-table-avatar">A</div>
|
|
||||||
<div>
|
|
||||||
<div className="admin-table-name">Jan Novák</div>
|
|
||||||
<div className="admin-table-username">@jan</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>jan@email.cz</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-admin">
|
|
||||||
Administrátor
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span className="admin-badge admin-badge-active">
|
|
||||||
Aktivní
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">✎</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
export default function VehiclesFixture() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="admin-page-header">
|
|
||||||
<h1 className="admin-page-title">Vozidla</h1>
|
|
||||||
<button className="admin-btn admin-btn-primary">
|
|
||||||
+ Přidat vozidlo
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card">
|
|
||||||
<div className="admin-card-body">
|
|
||||||
<div className="admin-search-bar mb-4">
|
|
||||||
<input className="admin-form-input" placeholder="" />
|
|
||||||
</div>
|
|
||||||
<div className="admin-table-responsive">
|
|
||||||
<table className="admin-table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Značka</th>
|
|
||||||
<th>Model</th>
|
|
||||||
<th>SPZ</th>
|
|
||||||
<th>Rok</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 5 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>Škoda</td>
|
|
||||||
<td>Octavia</td>
|
|
||||||
<td className="admin-mono">1A2 3456</td>
|
|
||||||
<td>2024</td>
|
|
||||||
<td>
|
|
||||||
<div className="admin-table-actions">
|
|
||||||
<button className="admin-btn-icon">✎</button>
|
|
||||||
<button className="admin-btn-icon danger">🗑</button>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from "react";
|
import { useState, useEffect, useCallback, useRef } from "react";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import apiFetch from "../utils/api";
|
import apiFetch from "../utils/api";
|
||||||
|
import { isHoliday } from "../../utils/czech-holidays";
|
||||||
import {
|
import {
|
||||||
calcProjectMinutesTotal,
|
calcProjectMinutesTotal,
|
||||||
calcFormWorkMinutes,
|
calcFormWorkMinutes,
|
||||||
@@ -10,9 +11,17 @@ import {
|
|||||||
formatDate,
|
formatDate,
|
||||||
formatMinutes,
|
formatMinutes,
|
||||||
getLeaveTypeName,
|
getLeaveTypeName,
|
||||||
getLeaveTypeBadgeClass,
|
|
||||||
formatTimeOrDatetimePrint,
|
formatTimeOrDatetimePrint,
|
||||||
calculateWorkMinutesPrint,
|
calculateWorkMinutesPrint,
|
||||||
|
isWeekendDate,
|
||||||
|
getDaysInMonth,
|
||||||
|
shiftDateForMonth,
|
||||||
|
formatHoursDecimal,
|
||||||
|
calculateNightMinutes,
|
||||||
|
normalizeDateStr,
|
||||||
|
holidaysInMonth,
|
||||||
|
calculateFreeHolidayHours,
|
||||||
|
getCzechWeekday,
|
||||||
} from "../utils/attendanceHelpers";
|
} from "../utils/attendanceHelpers";
|
||||||
import type {
|
import type {
|
||||||
ShiftFormData,
|
ShiftFormData,
|
||||||
@@ -29,7 +38,7 @@ interface AlertContext {
|
|||||||
alert: { success: (msg: string) => void; error: (msg: string) => void };
|
alert: { success: (msg: string) => void; error: (msg: string) => void };
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AttendanceRecord {
|
export interface AttendanceRecord {
|
||||||
id: number;
|
id: number;
|
||||||
user_id: number;
|
user_id: number;
|
||||||
shift_date: string;
|
shift_date: string;
|
||||||
@@ -39,7 +48,7 @@ interface AttendanceRecord {
|
|||||||
departure_time?: string | null;
|
departure_time?: string | null;
|
||||||
break_start?: string | null;
|
break_start?: string | null;
|
||||||
break_end?: string | null;
|
break_end?: string | null;
|
||||||
notes?: string;
|
notes?: string | null;
|
||||||
project_id?: number | null;
|
project_id?: number | null;
|
||||||
project_name?: string;
|
project_name?: string;
|
||||||
project_logs?: Array<{
|
project_logs?: Array<{
|
||||||
@@ -73,7 +82,6 @@ interface UserTotal {
|
|||||||
working: boolean;
|
working: boolean;
|
||||||
vacation_hours: number;
|
vacation_hours: number;
|
||||||
sick_hours: number;
|
sick_hours: number;
|
||||||
holiday_hours: number;
|
|
||||||
unpaid_hours: number;
|
unpaid_hours: number;
|
||||||
overtime: number;
|
overtime: number;
|
||||||
missing: number;
|
missing: number;
|
||||||
@@ -81,6 +89,16 @@ interface UserTotal {
|
|||||||
business_days: number;
|
business_days: number;
|
||||||
worked_hours: number;
|
worked_hours: number;
|
||||||
covered: number;
|
covered: number;
|
||||||
|
// mzda-style summary fields (set by computeUserTotals)
|
||||||
|
worked_hours_raw?: number;
|
||||||
|
odpracovano?: number;
|
||||||
|
vcetne_svatku?: number;
|
||||||
|
prescas?: number;
|
||||||
|
svatek?: number;
|
||||||
|
weekend_hours?: number;
|
||||||
|
night_minutes?: number;
|
||||||
|
worked_holiday_hours?: number; // sum of hours worked ON a holiday
|
||||||
|
records?: AttendanceRecord[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LeaveBalance {
|
interface LeaveBalance {
|
||||||
@@ -121,6 +139,11 @@ const combineDatetime = (date: string, time: string): string | null =>
|
|||||||
/**
|
/**
|
||||||
* Compute per-user totals from raw attendance records.
|
* Compute per-user totals from raw attendance records.
|
||||||
* This replaces the server-side `user_totals` that the PHP backend returned.
|
* This replaces the server-side `user_totals` that the PHP backend returned.
|
||||||
|
*
|
||||||
|
* Adds mzda-style summary fields (odpracovano, vcetne_svatku, prescas, svatek,
|
||||||
|
* weekend_hours, night_minutes) and a fund computed as
|
||||||
|
* (rawBizDays + holidayCount) × 8 — holidays count as work days for fund
|
||||||
|
* purposes, per the mzda.pdf model.
|
||||||
*/
|
*/
|
||||||
function computeUserTotals(
|
function computeUserTotals(
|
||||||
records: AttendanceRecord[],
|
records: AttendanceRecord[],
|
||||||
@@ -128,6 +151,7 @@ function computeUserTotals(
|
|||||||
month: string,
|
month: string,
|
||||||
): Record<string, UserTotal> {
|
): Record<string, UserTotal> {
|
||||||
const totals: Record<string, UserTotal> = {};
|
const totals: Record<string, UserTotal> = {};
|
||||||
|
const recordsByUser = new Map<number, AttendanceRecord[]>();
|
||||||
|
|
||||||
for (const rec of records) {
|
for (const rec of records) {
|
||||||
const uid = String(rec.user_id);
|
const uid = String(rec.user_id);
|
||||||
@@ -144,7 +168,6 @@ function computeUserTotals(
|
|||||||
working: false,
|
working: false,
|
||||||
vacation_hours: 0,
|
vacation_hours: 0,
|
||||||
sick_hours: 0,
|
sick_hours: 0,
|
||||||
holiday_hours: 0,
|
|
||||||
unpaid_hours: 0,
|
unpaid_hours: 0,
|
||||||
overtime: 0,
|
overtime: 0,
|
||||||
missing: 0,
|
missing: 0,
|
||||||
@@ -152,15 +175,23 @@ function computeUserTotals(
|
|||||||
business_days: 0,
|
business_days: 0,
|
||||||
worked_hours: 0,
|
worked_hours: 0,
|
||||||
covered: 0,
|
covered: 0,
|
||||||
|
records: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const t = totals[uid];
|
const t = totals[uid];
|
||||||
|
t.records!.push(rec);
|
||||||
|
if (!recordsByUser.has(rec.user_id)) recordsByUser.set(rec.user_id, []);
|
||||||
|
recordsByUser.get(rec.user_id)!.push(rec);
|
||||||
|
|
||||||
const leaveType = rec.leave_type || "work";
|
const leaveType = rec.leave_type || "work";
|
||||||
|
|
||||||
if (leaveType === "work") {
|
if (leaveType === "work") {
|
||||||
// Only work records contribute to "minutes" (matching PHP calculateUserTotals)
|
// Only work records contribute to "minutes" (matching PHP calculateUserTotals)
|
||||||
t.minutes += calculateWorkMinutes(rec);
|
t.minutes += calculateWorkMinutes({
|
||||||
|
...rec,
|
||||||
|
notes: rec.notes ?? undefined,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const leaveHours = Number(rec.leave_hours) || 8;
|
const leaveHours = Number(rec.leave_hours) || 8;
|
||||||
switch (leaveType) {
|
switch (leaveType) {
|
||||||
@@ -170,12 +201,14 @@ function computeUserTotals(
|
|||||||
case "sick":
|
case "sick":
|
||||||
t.sick_hours += leaveHours;
|
t.sick_hours += leaveHours;
|
||||||
break;
|
break;
|
||||||
case "holiday":
|
|
||||||
t.holiday_hours += leaveHours;
|
|
||||||
break;
|
|
||||||
case "unpaid":
|
case "unpaid":
|
||||||
t.unpaid_hours += leaveHours;
|
t.unpaid_hours += leaveHours;
|
||||||
break;
|
break;
|
||||||
|
// "holiday" leave_type is no longer selectable in the UI — it is
|
||||||
|
// auto-computed from Czech public holidays (see freeHolidayHours
|
||||||
|
// below). Old records may still exist in the DB; treat them as a
|
||||||
|
// paid non-work entry and skip so they don't double-count with
|
||||||
|
// the auto-detected svátek.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +223,10 @@ function computeUserTotals(
|
|||||||
const yr = parseInt(yearStr, 10);
|
const yr = parseInt(yearStr, 10);
|
||||||
const mo = parseInt(monthStr, 10) - 1;
|
const mo = parseInt(monthStr, 10) - 1;
|
||||||
|
|
||||||
// Count business days in month (Mon-Fri)
|
// Count Mon-Fri business days in month (INCLUDING holidays).
|
||||||
|
// The fund is rawBizDays × 8 — holidays stay in the count so the
|
||||||
|
// user is paid for them via the fund. Svátek (free holiday hours) is
|
||||||
|
// computed separately as "8h per holiday the user did not work".
|
||||||
let rawBizDays = 0;
|
let rawBizDays = 0;
|
||||||
const cur = new Date(yr, mo, 1);
|
const cur = new Date(yr, mo, 1);
|
||||||
while (cur.getMonth() === mo) {
|
while (cur.getMonth() === mo) {
|
||||||
@@ -199,23 +235,100 @@ function computeUserTotals(
|
|||||||
cur.setDate(cur.getDate() + 1);
|
cur.setDate(cur.getDate() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const holidayDates = holidaysInMonth(yr, mo + 1);
|
||||||
|
const holidayCount = holidayDates.length;
|
||||||
|
|
||||||
for (const uid of Object.keys(totals)) {
|
for (const uid of Object.keys(totals)) {
|
||||||
const t = totals[uid];
|
const t = totals[uid];
|
||||||
// Subtract holiday days from business days for this user
|
const userRecords = recordsByUser.get(Number(uid)) || [];
|
||||||
const holidayDays = Math.round(t.holiday_hours / 8);
|
|
||||||
const bizDays = Math.max(0, rawBizDays - holidayDays);
|
// Odpracováno: floor(worked / 8) × 8 — round to whole days, drop remainder.
|
||||||
const fund = bizDays * 8;
|
const workedHoursRaw = t.minutes / 60;
|
||||||
const workedHours = Math.round((t.minutes / 60) * 10) / 10;
|
const odpracovano = Math.floor(workedHoursRaw / 8) * 8;
|
||||||
// Covered = worked + vacation + sick (NOT holiday/unpaid — matching PHP)
|
|
||||||
const leaveHours = t.vacation_hours + t.sick_hours;
|
// So/Ne: sum of worked hours on Sat/Sun (raw, not rounded).
|
||||||
const covered = Math.round((workedHours + leaveHours) * 10) / 10;
|
const weekendHours = userRecords
|
||||||
|
.filter(
|
||||||
|
(r) =>
|
||||||
|
(r.leave_type || "work") === "work" && isWeekendDate(r.shift_date),
|
||||||
|
)
|
||||||
|
.reduce((sum, r) => sum + calculateWorkMinutesPrint(r) / 60, 0);
|
||||||
|
|
||||||
|
// Práce v noci: minutes in 22:00-06:00 across all work records.
|
||||||
|
const nightMinutes = userRecords
|
||||||
|
.filter((r) => (r.leave_type || "work") === "work")
|
||||||
|
.reduce((sum, r) => sum + calculateNightMinutes(r), 0);
|
||||||
|
|
||||||
|
// Svátek (free): 8h per holiday the user did not work.
|
||||||
|
const freeHolidayHours = calculateFreeHolidayHours(
|
||||||
|
userRecords,
|
||||||
|
holidayDates,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Worked holidays: holidays the user DID work (counted toward
|
||||||
|
// Odpracováno, but their 8h must not flow into Přesčas).
|
||||||
|
const workedDatesSet = new Set(
|
||||||
|
userRecords
|
||||||
|
.filter((r) => (r.leave_type || "work") === "work")
|
||||||
|
.map((r) => normalizeDateStr(r.shift_date))
|
||||||
|
.filter(Boolean),
|
||||||
|
);
|
||||||
|
let workedHolidayCount = 0;
|
||||||
|
let workedHolidayHours = 0;
|
||||||
|
for (const hd of holidayDates) {
|
||||||
|
if (workedDatesSet.has(hd)) workedHolidayCount++;
|
||||||
|
}
|
||||||
|
// Sum the actual hours worked on holiday dates (for the KPI card's
|
||||||
|
// "fond used" total = real_work + vacation + worked_holiday + free_holiday).
|
||||||
|
for (const r of userRecords) {
|
||||||
|
if ((r.leave_type || "work") !== "work") continue;
|
||||||
|
const d = normalizeDateStr(r.shift_date);
|
||||||
|
if (d && holidayDates.includes(d)) {
|
||||||
|
workedHolidayHours += calculateWorkMinutesPrint(r) / 60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fund = Mon-Fri × 8h. Holidays count as work days (already in rawBizDays).
|
||||||
|
const fund = rawBizDays * 8;
|
||||||
|
|
||||||
|
// Včetně svátků a přesčasů (mzda formula):
|
||||||
|
// odpracovano + vacation + remainder − min(freeHols, workedHols × 8)
|
||||||
|
//
|
||||||
|
// Two-way logic matching the print:
|
||||||
|
// • If the user worked at least one holiday, that worked holiday's
|
||||||
|
// 8h is in Odpracováno and shouldn't also count as Přesčas. We
|
||||||
|
// subtract one 8h per worked holiday (capped at the total free
|
||||||
|
// holiday hours — can't go negative).
|
||||||
|
// • If the user did NOT work any holiday, no adjustment is needed:
|
||||||
|
// all unworked holidays are already paid via the Svátek line and
|
||||||
|
// don't flow into Přesčas, so vcetne_svatku just equals the
|
||||||
|
// work + vacation + remainder.
|
||||||
|
const remainder = workedHoursRaw - odpracovano;
|
||||||
|
const holidayAdj = Math.min(freeHolidayHours, workedHolidayCount * 8);
|
||||||
|
const vcetneSv = odpracovano - holidayAdj + t.vacation_hours + remainder;
|
||||||
|
|
||||||
|
// Přesčas = vcetneSv − odpracovano (derived).
|
||||||
|
const prescas = vcetneSv - odpracovano;
|
||||||
|
|
||||||
|
// Legacy fields (kept so existing UI doesn't break).
|
||||||
|
const workedHours = Math.round(workedHoursRaw * 10) / 10;
|
||||||
|
const covered = Math.round((workedHours + t.vacation_hours) * 10) / 10;
|
||||||
|
|
||||||
t.fund = fund;
|
t.fund = fund;
|
||||||
t.business_days = bizDays;
|
t.business_days = rawBizDays;
|
||||||
t.worked_hours = workedHours;
|
t.worked_hours = workedHours;
|
||||||
t.covered = covered;
|
t.covered = covered;
|
||||||
t.missing = Math.max(0, Math.round((fund - covered) * 10) / 10);
|
t.missing = Math.max(0, Math.round((fund - covered) * 10) / 10);
|
||||||
t.overtime = Math.max(0, Math.round((covered - fund) * 10) / 10);
|
t.overtime = Math.max(0, Math.round((covered - fund) * 10) / 10);
|
||||||
|
|
||||||
|
t.worked_hours_raw = workedHoursRaw;
|
||||||
|
t.odpracovano = odpracovano;
|
||||||
|
t.vcetne_svatku = vcetneSv;
|
||||||
|
t.prescas = prescas;
|
||||||
|
t.svatek = freeHolidayHours;
|
||||||
|
t.weekend_hours = weekendHours;
|
||||||
|
t.night_minutes = nightMinutes;
|
||||||
|
t.worked_holiday_hours = workedHolidayHours;
|
||||||
}
|
}
|
||||||
|
|
||||||
return totals;
|
return totals;
|
||||||
@@ -234,14 +347,6 @@ function escapeHtml(str: string): string {
|
|||||||
.replace(/'/g, "'");
|
.replace(/'/g, "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderFundStatus(userData: Record<string, any>): string {
|
|
||||||
if (userData.overtime > 0)
|
|
||||||
return `<span class="leave-badge badge-overtime">+${escapeHtml(String(userData.overtime))}h přesčas</span>`;
|
|
||||||
if (userData.missing > 0)
|
|
||||||
return `<span style="color:#dc2626">−${escapeHtml(String(userData.missing))}h</span>`;
|
|
||||||
return '<span style="color:#16a34a">splněno</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildProjectLogsHtml(record: Record<string, any>): string {
|
function buildProjectLogsHtml(record: Record<string, any>): string {
|
||||||
if (record.project_logs && record.project_logs.length > 0) {
|
if (record.project_logs && record.project_logs.length > 0) {
|
||||||
return record.project_logs
|
return record.project_logs
|
||||||
@@ -272,94 +377,220 @@ function buildProjectLogsHtml(record: Record<string, any>): string {
|
|||||||
return escapeHtml(record.project_name || "—");
|
return escapeHtml(record.project_name || "—");
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildLeaveSummaryHtml(
|
|
||||||
userId: string,
|
|
||||||
userData: Record<string, any>,
|
|
||||||
printData: Record<string, any>,
|
|
||||||
): string {
|
|
||||||
const bal = printData.leave_balances[userId];
|
|
||||||
let parts = `<strong>Dovolená ${escapeHtml(String(printData.year))}:</strong> Zbývá ${bal.vacation_remaining.toFixed(1)}h z ${bal.vacation_total}h`;
|
|
||||||
if (userData.vacation_hours > 0)
|
|
||||||
parts += ` | <span class="leave-badge badge-vacation">Tento měsíc: ${escapeHtml(String(userData.vacation_hours))}h</span>`;
|
|
||||||
if (userData.sick_hours > 0)
|
|
||||||
parts += ` | <span class="leave-badge badge-sick">Nemoc: ${escapeHtml(String(userData.sick_hours))}h</span>`;
|
|
||||||
if (userData.holiday_hours > 0)
|
|
||||||
parts += ` | <span class="leave-badge badge-holiday">Svátek: ${escapeHtml(String(userData.holiday_hours))}h</span>`;
|
|
||||||
if (userData.overtime > 0)
|
|
||||||
parts += ` | <span class="leave-badge badge-overtime">Přesčas: +${escapeHtml(String(userData.overtime))}h</span>`;
|
|
||||||
return `<div class="leave-summary">${parts}</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildUserSectionHtml(
|
function buildUserSectionHtml(
|
||||||
userId: string,
|
userId: string,
|
||||||
userData: Record<string, any>,
|
userData: Record<string, any>,
|
||||||
printData: Record<string, any>,
|
printData: Record<string, any>,
|
||||||
): string {
|
): string {
|
||||||
const leaveHtml = printData.leave_balances[userId]
|
// Build a date-keyed lookup of the user's records.
|
||||||
? buildLeaveSummaryHtml(userId, userData, printData)
|
const recordsByDate = new Map<string, Record<string, any>>();
|
||||||
: "";
|
for (const r of userData.records || []) {
|
||||||
|
recordsByDate.set(normalizeDateStr(r.shift_date), r);
|
||||||
|
}
|
||||||
|
|
||||||
const recordRows = (userData.records || [])
|
// Iterate one row per day of the month.
|
||||||
.map((record: any) => {
|
const [yearStr, monthStr] = printData.month.split("-");
|
||||||
const leaveType = record.leave_type || "work";
|
const yr = parseInt(yearStr, 10);
|
||||||
const isLeave = leaveType !== "work";
|
const mo = parseInt(monthStr, 10);
|
||||||
const workMinutes = calculateWorkMinutesPrint(record);
|
const daysInMonth = getDaysInMonth(yr, mo);
|
||||||
const hours = Math.floor(workMinutes / 60);
|
|
||||||
const mins = workMinutes % 60;
|
|
||||||
const breakCell =
|
|
||||||
isLeave || !record.break_start || !record.break_end
|
|
||||||
? "—"
|
|
||||||
: `${escapeHtml(formatTimeOrDatetimePrint(record.break_start, record.shift_date))} - ${escapeHtml(formatTimeOrDatetimePrint(record.break_end, record.shift_date))}`;
|
|
||||||
|
|
||||||
return `<tr>
|
const userRecords = (userData.records || []) as Record<string, any>[];
|
||||||
<td>${escapeHtml(formatDate(record.shift_date))}</td>
|
|
||||||
<td><span class="leave-badge ${escapeHtml(getLeaveTypeBadgeClass(leaveType))}">${escapeHtml(getLeaveTypeName(leaveType))}</span></td>
|
const rows: string[] = [];
|
||||||
|
for (let d = 1; d <= daysInMonth; d++) {
|
||||||
|
const dateStr = shiftDateForMonth(yr, mo, d);
|
||||||
|
const record = recordsByDate.get(dateStr);
|
||||||
|
const weekend = isWeekendDate(dateStr);
|
||||||
|
const holiday = isHoliday(dateStr);
|
||||||
|
const leaveType = (record?.leave_type as string) || "work";
|
||||||
|
const rowClasses = [
|
||||||
|
weekend && "weekend",
|
||||||
|
holiday && "holiday",
|
||||||
|
leaveType === "vacation" && "vacation",
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
|
if (!record) {
|
||||||
|
rows.push(`<tr class="${rowClasses}">
|
||||||
|
<td>${escapeHtml(formatDate(dateStr))}</td>
|
||||||
|
<td>${escapeHtml(getCzechWeekday(dateStr))}</td>
|
||||||
|
<td>—</td>
|
||||||
|
<td class="text-center">—</td>
|
||||||
|
<td class="text-center">—</td>
|
||||||
|
<td class="text-center">—</td>
|
||||||
|
<td class="text-center">—</td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLeave = leaveType !== "work";
|
||||||
|
const workMinutes = calculateWorkMinutesPrint(record);
|
||||||
|
const hours = Math.floor(workMinutes / 60);
|
||||||
|
const mins = workMinutes % 60;
|
||||||
|
const breakCell =
|
||||||
|
isLeave || !record.break_start || !record.break_end
|
||||||
|
? "—"
|
||||||
|
: `${escapeHtml(formatTimeOrDatetimePrint(record.break_start, record.shift_date))} - ${escapeHtml(formatTimeOrDatetimePrint(record.break_end, record.shift_date))}`;
|
||||||
|
|
||||||
|
let hoursCell: string;
|
||||||
|
if (workMinutes > 0 && !isLeave) {
|
||||||
|
hoursCell = `${hours}:${String(mins).padStart(2, "0")} (${formatHoursDecimal(workMinutes)})`;
|
||||||
|
} else if (isLeave) {
|
||||||
|
hoursCell = formatHoursDecimal((Number(record.leave_hours) || 8) * 60);
|
||||||
|
} else {
|
||||||
|
hoursCell = "—";
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.push(`<tr class="${rowClasses}">
|
||||||
|
<td>${escapeHtml(formatDate(dateStr))}</td>
|
||||||
|
<td>${escapeHtml(getCzechWeekday(dateStr))}</td>
|
||||||
|
<td>${escapeHtml(getLeaveTypeName(leaveType))}</td>
|
||||||
<td class="text-center">${isLeave ? "—" : escapeHtml(formatTimeOrDatetimePrint(record.arrival_time, record.shift_date))}</td>
|
<td class="text-center">${isLeave ? "—" : escapeHtml(formatTimeOrDatetimePrint(record.arrival_time, record.shift_date))}</td>
|
||||||
<td class="text-center">${breakCell}</td>
|
<td class="text-center">${breakCell}</td>
|
||||||
<td class="text-center">${isLeave ? "—" : escapeHtml(formatTimeOrDatetimePrint(record.departure_time, record.shift_date))}</td>
|
<td class="text-center">${isLeave ? "—" : escapeHtml(formatTimeOrDatetimePrint(record.departure_time, record.shift_date))}</td>
|
||||||
<td class="text-center">${workMinutes > 0 ? `${hours}:${String(mins).padStart(2, "0")}` : "—"}</td>
|
<td class="text-center">${hoursCell}</td>
|
||||||
<td style="font-size:8px">${buildProjectLogsHtml(record)}</td>
|
<td style="font-size:8px">${buildProjectLogsHtml(record)}</td>
|
||||||
<td>${escapeHtml(record.notes || "")}</td>
|
<td>${escapeHtml(record.notes || "")}</td>
|
||||||
</tr>`;
|
</tr>`);
|
||||||
})
|
}
|
||||||
.join("");
|
|
||||||
|
|
||||||
const fundRow =
|
// ----- mzda-style summary numbers (computed here, not from userData,
|
||||||
userData.fund !== null
|
// because the backend's getPrintData only returns the legacy fields). -----
|
||||||
? `<tr>
|
|
||||||
<td colspan="6" class="text-right">Fond měsíce:</td>
|
// Worked minutes: sum of calculateWorkMinutesPrint across work records.
|
||||||
<td class="text-center">${escapeHtml(String(userData.covered))}h / ${escapeHtml(String(userData.fund))}h</td>
|
let workedMinutes = 0;
|
||||||
<td colspan="2">${renderFundStatus(userData)}</td>
|
let weekendHoursRaw = 0;
|
||||||
</tr>`
|
let nightMinutes = 0;
|
||||||
: "";
|
for (const r of userRecords) {
|
||||||
|
const leaveType = r.leave_type || "work";
|
||||||
|
if (leaveType !== "work") continue;
|
||||||
|
const m = calculateWorkMinutesPrint(r);
|
||||||
|
workedMinutes += m;
|
||||||
|
if (isWeekendDate(r.shift_date)) {
|
||||||
|
weekendHoursRaw += m / 60;
|
||||||
|
}
|
||||||
|
nightMinutes += calculateNightMinutes(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum of vacation/sick/unpaid hours (in hours, not minutes). The
|
||||||
|
// "holiday" leave_type is auto-computed from Czech holidays further down
|
||||||
|
// and no longer selectable in the UI, so it's deliberately omitted here.
|
||||||
|
let vacationHours = 0,
|
||||||
|
sickHours = 0;
|
||||||
|
for (const r of userRecords) {
|
||||||
|
const lt = r.leave_type || "work";
|
||||||
|
if (lt === "work") continue;
|
||||||
|
const h = Number(r.leave_hours) || 8;
|
||||||
|
if (lt === "vacation") vacationHours += h;
|
||||||
|
else if (lt === "sick") sickHours += h;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Odpracováno: floor(worked / 8) × 8.
|
||||||
|
const workedHoursRaw = workedMinutes / 60;
|
||||||
|
const odpracovano = Math.floor(workedHoursRaw / 8) * 8;
|
||||||
|
|
||||||
|
// Free Svátek: 8h per holiday date the user did not work.
|
||||||
|
const holidayDates = holidaysInMonth(yr, mo);
|
||||||
|
const holidayCount = holidayDates.length;
|
||||||
|
const workedDates = new Set(
|
||||||
|
userRecords
|
||||||
|
.filter((r) => (r.leave_type || "work") === "work")
|
||||||
|
.map((r) => normalizeDateStr(r.shift_date))
|
||||||
|
.filter(Boolean),
|
||||||
|
);
|
||||||
|
let freeHolidayHours = 0;
|
||||||
|
let workedHolidayCount = 0;
|
||||||
|
for (const hd of holidayDates) {
|
||||||
|
if (workedDates.has(hd)) workedHolidayCount++;
|
||||||
|
else freeHolidayHours += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fund: count Mon-Fri (incl. holidays) × 8h.
|
||||||
|
let rawBizDays = 0;
|
||||||
|
const cur = new Date(yr, mo - 1, 1);
|
||||||
|
while (cur.getMonth() === mo - 1) {
|
||||||
|
const dow = cur.getDay();
|
||||||
|
if (dow !== 0 && dow !== 6) rawBizDays++;
|
||||||
|
cur.setDate(cur.getDate() + 1);
|
||||||
|
}
|
||||||
|
const businessDays = rawBizDays;
|
||||||
|
const fund = businessDays * 8;
|
||||||
|
|
||||||
|
// Včetně svátků a přesčasů: floor(worked/8)*8 − worked_holiday*8 + vacation + remainder.
|
||||||
|
// (A worked holiday counts toward Odpracováno, but its 8h should not flow
|
||||||
|
// into Přesčas — so we subtract it here. Unworked holidays are paid via
|
||||||
|
// the fund and don't affect this line.)
|
||||||
|
const remainder = workedHoursRaw - odpracovano;
|
||||||
|
const vcetneSv =
|
||||||
|
odpracovano - workedHolidayCount * 8 + vacationHours + remainder;
|
||||||
|
|
||||||
|
// Přesčas = #2 - #1.
|
||||||
|
const prescas = vcetneSv - odpracovano;
|
||||||
|
|
||||||
|
const summaryHtml = `<div class="user-summary">
|
||||||
|
<div class="summary-row">
|
||||||
|
<span class="summary-label">Odpracováno:</span>
|
||||||
|
<span class="summary-value">${formatHoursDecimal(odpracovano * 60)}h</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-row">
|
||||||
|
<span class="summary-label">Odpracováno včetně svátků a přesčasů:</span>
|
||||||
|
<span class="summary-value">${formatHoursDecimal(vcetneSv * 60)}h</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-row">
|
||||||
|
<span class="summary-label">Dovolená:</span>
|
||||||
|
<span class="summary-value">${formatHoursDecimal(vacationHours * 60)}h</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-row">
|
||||||
|
<span class="summary-label">Přesčas:</span>
|
||||||
|
<span class="summary-value">${formatHoursDecimal(prescas * 60)}h</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-row">
|
||||||
|
<span class="summary-label">Svátek:</span>
|
||||||
|
<span class="summary-value">${formatHoursDecimal(freeHolidayHours * 60)}h</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-row">
|
||||||
|
<span class="summary-label">So/Ne:</span>
|
||||||
|
<span class="summary-value">${formatHoursDecimal(weekendHoursRaw * 60)}h</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-row">
|
||||||
|
<span class="summary-label">Práce v noci:</span>
|
||||||
|
<span class="summary-value">${formatHoursDecimal(nightMinutes)}h</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-row summary-fund">
|
||||||
|
<span class="summary-label">Fond měsíce:</span>
|
||||||
|
<span class="summary-value">${formatHoursDecimal(fund * 60)}h (${businessDays} dnů)</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend">
|
||||||
|
<span class="legend-item"><span class="legend-swatch weekend"></span>Víkend (So/Ne)</span>
|
||||||
|
<span class="legend-item"><span class="legend-swatch holiday"></span>Svátek</span>
|
||||||
|
<span class="legend-item"><span class="legend-swatch weekend-holiday"></span>Víkend + svátek</span>
|
||||||
|
<span class="legend-item"><span class="legend-swatch vacation"></span>Dovolená</span>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
return `<div class="user-section">
|
return `<div class="user-section">
|
||||||
<div class="user-header">
|
<div class="user-header">
|
||||||
<h3>${escapeHtml(userData.name)}</h3>
|
<h3>${escapeHtml(userData.name)}</h3>
|
||||||
<span class="total">Odpracováno: ${escapeHtml(formatMinutes(userData.minutes))} h</span>
|
<span class="total">Odpracováno: ${escapeHtml(formatMinutes(userData.minutes))} h</span>
|
||||||
</div>
|
</div>
|
||||||
${leaveHtml}
|
|
||||||
<table>
|
<table>
|
||||||
<thead><tr>
|
<thead><tr>
|
||||||
<th style="width:70px">Datum</th>
|
<th style="width:55px">Datum</th>
|
||||||
<th style="width:70px">Typ</th>
|
<th style="width:55px">Den</th>
|
||||||
<th class="text-center" style="width:70px">Příchod</th>
|
<th style="width:60px">Typ</th>
|
||||||
<th class="text-center" style="width:90px">Pauza</th>
|
<th class="text-center" style="width:55px">Příchod</th>
|
||||||
<th class="text-center" style="width:70px">Odchod</th>
|
<th class="text-center" style="width:80px">Pauza</th>
|
||||||
<th class="text-center" style="width:80px">Hodiny</th>
|
<th class="text-center" style="width:55px">Odchod</th>
|
||||||
|
<th class="text-center" style="width:85px">Hodiny</th>
|
||||||
<th>Projekty</th>
|
<th>Projekty</th>
|
||||||
<th>Poznámka</th>
|
<th>Poznámka</th>
|
||||||
</tr></thead>
|
</tr></thead>
|
||||||
<tbody>${recordRows}</tbody>
|
<tbody>${rows.join("")}</tbody>
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<td colspan="6" class="text-right">Odpracováno:</td>
|
|
||||||
<td class="text-center">${escapeHtml(formatMinutes(userData.minutes))} h</td>
|
|
||||||
<td colspan="2"></td>
|
|
||||||
</tr>
|
|
||||||
${fundRow}
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
</table>
|
||||||
|
${summaryHtml}
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,7 +608,13 @@ function buildPrintHtml(
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Docházka - ${escapeHtml(pData.month_name)}</title>
|
<title>Docházka - ${escapeHtml(pData.month_name)}</title>
|
||||||
<style>
|
<style>
|
||||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-print-color-adjust: exact;
|
||||||
|
print-color-adjust: exact;
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
font-size: 11px; line-height: 1.4; color: #000; background: #fff; padding: 15mm;
|
font-size: 11px; line-height: 1.4; color: #000; background: #fff; padding: 15mm;
|
||||||
@@ -407,19 +644,47 @@ function buildPrintHtml(
|
|||||||
.user-section th { background: #333; color: #fff; font-weight: 600; font-size: 10px; text-transform: uppercase; }
|
.user-section th { background: #333; color: #fff; font-weight: 600; font-size: 10px; text-transform: uppercase; }
|
||||||
.user-section td { font-size: 10px; }
|
.user-section td { font-size: 10px; }
|
||||||
.user-section tr:nth-child(even) { background: #f9f9f9; }
|
.user-section tr:nth-child(even) { background: #f9f9f9; }
|
||||||
|
/* Weekend and holiday row highlighting — must come after the
|
||||||
|
:nth-child(even) rule and use higher specificity (tr.X td) to win. */
|
||||||
|
.user-section tr.weekend td { background: #fde68a; }
|
||||||
|
.user-section tr.holiday td { background: #bbf7d0; }
|
||||||
|
.user-section tr.weekend.holiday td { background: #fcd34d; }
|
||||||
|
.user-section tr.vacation td { background: #bfdbfe; }
|
||||||
.text-center { text-align: center; }
|
.text-center { text-align: center; }
|
||||||
.text-right { text-align: right; }
|
.text-right { text-align: right; }
|
||||||
.user-section tfoot td { background: #eee; font-weight: 600; }
|
|
||||||
.leave-badge { display: inline-block; padding: 2px 6px; border-radius: 3px; font-size: 9px; font-weight: 500; }
|
.leave-badge { display: inline-block; padding: 2px 6px; border-radius: 3px; font-size: 9px; font-weight: 500; }
|
||||||
.badge-vacation { background: #dbeafe; color: #1d4ed8; }
|
.badge-vacation { background: #dbeafe; color: #1d4ed8; }
|
||||||
.badge-sick { background: #fee2e2; color: #dc2626; }
|
.badge-sick { background: #fee2e2; color: #dc2626; }
|
||||||
.badge-holiday { background: #dcfce7; color: #16a34a; }
|
.badge-holiday { background: #dcfce7; color: #16a34a; }
|
||||||
.badge-unpaid { background: #f3f4f6; color: #6b7280; }
|
.badge-unpaid { background: #f3f4f6; color: #6b7280; }
|
||||||
.badge-overtime { background: #fef3c7; color: #d97706; }
|
.badge-overtime { background: #fef3c7; color: #d97706; }
|
||||||
.leave-summary {
|
.user-summary {
|
||||||
margin-top: 10px; padding: 8px 15px; background: #f9f9f9;
|
margin-top: 10px; padding: 10px 15px;
|
||||||
border: 1px solid #ddd; font-size: 10px;
|
background: #f9f9f9; border: 1px solid #ddd; font-size: 10px;
|
||||||
}
|
}
|
||||||
|
.user-summary .summary-row {
|
||||||
|
display: flex; justify-content: space-between; padding: 2px 0;
|
||||||
|
}
|
||||||
|
.user-summary .summary-label { color: #555; }
|
||||||
|
.user-summary .summary-value { font-weight: 600; }
|
||||||
|
.user-summary .summary-fund {
|
||||||
|
border-top: 1px solid #ddd; margin-top: 4px; padding-top: 6px;
|
||||||
|
}
|
||||||
|
.legend {
|
||||||
|
display: flex; flex-wrap: wrap; gap: 14px;
|
||||||
|
margin-top: 8px; padding-top: 6px;
|
||||||
|
border-top: 1px dashed #ddd;
|
||||||
|
font-size: 9px; color: #555;
|
||||||
|
}
|
||||||
|
.legend-item { display: inline-flex; align-items: center; gap: 5px; }
|
||||||
|
.legend-swatch {
|
||||||
|
display: inline-block; width: 12px; height: 12px;
|
||||||
|
border: 1px solid #333; vertical-align: middle;
|
||||||
|
}
|
||||||
|
.legend-swatch.weekend { background: #fde68a; }
|
||||||
|
.legend-swatch.holiday { background: #bbf7d0; }
|
||||||
|
.legend-swatch.weekend-holiday { background: #fcd34d; }
|
||||||
|
.legend-swatch.vacation { background: #bfdbfe; }
|
||||||
.print-wrapper-table { width: 100%; border-collapse: collapse; border: none; }
|
.print-wrapper-table { width: 100%; border-collapse: collapse; border: none; }
|
||||||
.print-wrapper-table > thead > tr > td,
|
.print-wrapper-table > thead > tr > td,
|
||||||
.print-wrapper-table > tbody > tr > td { padding: 0; border: none; background: none; }
|
.print-wrapper-table > tbody > tr > td { padding: 0; border: none; background: none; }
|
||||||
@@ -664,7 +929,15 @@ export default function useAttendanceAdmin({ alert }: AlertContext) {
|
|||||||
formData: ShiftFormData,
|
formData: ShiftFormData,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
const totalWork = calcFormWorkMinutes(formData);
|
const totalWork = calcFormWorkMinutes(formData);
|
||||||
const totalProject = calcProjectMinutesTotal(logs);
|
const totalProject = calcProjectMinutesTotal(
|
||||||
|
logs.map((l) => ({
|
||||||
|
...l,
|
||||||
|
project_id:
|
||||||
|
l.project_id !== "" && l.project_id != null
|
||||||
|
? Number(l.project_id)
|
||||||
|
: undefined,
|
||||||
|
})),
|
||||||
|
);
|
||||||
if (totalWork > 0 && totalProject !== totalWork) {
|
if (totalWork > 0 && totalProject !== totalWork) {
|
||||||
const wH = Math.floor(totalWork / 60);
|
const wH = Math.floor(totalWork / 60);
|
||||||
const wM = totalWork % 60;
|
const wM = totalWork % 60;
|
||||||
@@ -869,7 +1142,7 @@ export default function useAttendanceAdmin({ alert }: AlertContext) {
|
|||||||
const userName = record.users
|
const userName = record.users
|
||||||
? `${record.users.first_name} ${record.users.last_name}`.trim() ||
|
? `${record.users.first_name} ${record.users.last_name}`.trim() ||
|
||||||
record.users.username
|
record.users.username
|
||||||
: ((record as Record<string, unknown>).user_name as string) ||
|
: ((record as unknown as Record<string, unknown>).user_name as string) ||
|
||||||
`User #${record.user_id}`;
|
`User #${record.user_id}`;
|
||||||
const enriched = { ...record, user_name: userName };
|
const enriched = { ...record, user_name: userName };
|
||||||
setEditingRecord(enriched);
|
setEditingRecord(enriched);
|
||||||
|
|||||||
24
src/admin/hooks/useReducedMotion.ts
Normal file
24
src/admin/hooks/useReducedMotion.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when the user has expressed a preference for reduced motion
|
||||||
|
* (OS-level setting: `prefers-reduced-motion: reduce`).
|
||||||
|
*
|
||||||
|
* SSR-safe: starts as `false` (the default state), so the first render uses
|
||||||
|
* the normal animation. The effect then reconciles with the live matchMedia
|
||||||
|
* state on the client and re-renders if needed.
|
||||||
|
*/
|
||||||
|
export default function useReducedMotion(): boolean {
|
||||||
|
const [reduced, setReduced] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window === "undefined" || !window.matchMedia) return;
|
||||||
|
const mq = window.matchMedia("(prefers-reduced-motion: reduce)");
|
||||||
|
setReduced(mq.matches);
|
||||||
|
const onChange = () => setReduced(mq.matches);
|
||||||
|
mq.addEventListener("change", onChange);
|
||||||
|
return () => mq.removeEventListener("change", onChange);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return reduced;
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ export interface InvoiceItem {
|
|||||||
quantity: number;
|
quantity: number;
|
||||||
unit: string;
|
unit: string;
|
||||||
unit_price: number;
|
unit_price: number;
|
||||||
|
vat_rate?: number;
|
||||||
is_included_in_total: boolean;
|
is_included_in_total: boolean;
|
||||||
position?: number;
|
position?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
113
src/admin/lib/queries/mutations.ts
Normal file
113
src/admin/lib/queries/mutations.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import {
|
||||||
|
useMutation,
|
||||||
|
useQueryClient,
|
||||||
|
type UseMutationOptions,
|
||||||
|
} from "@tanstack/react-query";
|
||||||
|
import apiFetch from "../../utils/api";
|
||||||
|
|
||||||
|
export interface ApiError extends Error {
|
||||||
|
status?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HttpMethod = "POST" | "PUT" | "PATCH" | "DELETE";
|
||||||
|
|
||||||
|
interface ApiResponseBody<T> {
|
||||||
|
success: boolean;
|
||||||
|
data?: T;
|
||||||
|
error?: string;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function performMutation<TIn, TOut>(opts: {
|
||||||
|
url: string | ((input: TIn) => string);
|
||||||
|
method: HttpMethod | ((input: TIn) => HttpMethod);
|
||||||
|
body?: TIn;
|
||||||
|
}): Promise<TOut> {
|
||||||
|
const url =
|
||||||
|
typeof opts.url === "function" ? opts.url(opts.body as TIn) : opts.url;
|
||||||
|
const method =
|
||||||
|
typeof opts.method === "function"
|
||||||
|
? opts.method(opts.body as TIn)
|
||||||
|
: opts.method;
|
||||||
|
const response = await apiFetch(url, {
|
||||||
|
method,
|
||||||
|
headers:
|
||||||
|
opts.body !== undefined
|
||||||
|
? { "Content-Type": "application/json" }
|
||||||
|
: undefined,
|
||||||
|
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.status === 401) {
|
||||||
|
const err: ApiError = new Error("Unauthorized");
|
||||||
|
err.status = 401;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result: ApiResponseBody<TOut>;
|
||||||
|
try {
|
||||||
|
result = (await response.json()) as ApiResponseBody<TOut>;
|
||||||
|
} catch {
|
||||||
|
throw new Error("Invalid JSON response");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok || !result.success) {
|
||||||
|
const err: ApiError = new Error(
|
||||||
|
result.error || `Request failed (${response.status})`,
|
||||||
|
);
|
||||||
|
err.status = response.status;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.data as TOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ApiMutationOptions<TIn, TOut> {
|
||||||
|
url: string | ((input: TIn) => string);
|
||||||
|
method: HttpMethod | ((input: TIn) => HttpMethod);
|
||||||
|
/** Query-key prefixes to invalidate on success. Broad per CLAUDE.md. */
|
||||||
|
invalidate?: readonly string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook that wraps `useMutation` with `apiFetch` and broad-invalidation.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* const createCategory = useApiMutation<CategoryForm, WarehouseCategory>({
|
||||||
|
* url: "/api/admin/warehouse/categories",
|
||||||
|
* method: "POST",
|
||||||
|
* invalidate: ["warehouse"],
|
||||||
|
* });
|
||||||
|
* createCategory.mutate(form, {
|
||||||
|
* onSuccess: () => { ... },
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* The returned `data` from a successful mutation is `TOut`. Errors are thrown
|
||||||
|
* as `ApiError` (with `.status` attached) so callers can branch on HTTP code.
|
||||||
|
*/
|
||||||
|
export function useApiMutation<TIn, TOut>(
|
||||||
|
opts: ApiMutationOptions<TIn, TOut> &
|
||||||
|
Omit<UseMutationOptions<TOut, ApiError, TIn>, "mutationFn">,
|
||||||
|
) {
|
||||||
|
const { url, method, invalidate, onSuccess, ...rest } = opts;
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation<TOut, ApiError, TIn, unknown>({
|
||||||
|
mutationFn: (input: TIn) =>
|
||||||
|
performMutation<TIn, TOut>({ url, method, body: input }),
|
||||||
|
...rest,
|
||||||
|
onSuccess: (data, variables, onMutateResult, context) => {
|
||||||
|
if (invalidate) {
|
||||||
|
for (const key of invalidate) {
|
||||||
|
queryClient.invalidateQueries({ queryKey: [key] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Forward to user-provided onSuccess with the 4-arg signature expected by TanStack.
|
||||||
|
(
|
||||||
|
onSuccess as
|
||||||
|
| ((d: TOut, v: TIn, r: unknown, c: unknown) => void)
|
||||||
|
| undefined
|
||||||
|
)?.(data, variables, onMutateResult, context);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ export interface ScopeSection {
|
|||||||
export interface ScopeTemplate {
|
export interface ScopeTemplate {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
scope_template_sections?: ScopeSection[];
|
scope_template_sections?: ScopeSection[];
|
||||||
}
|
}
|
||||||
@@ -70,6 +71,8 @@ export const offerListOptions = (filters: {
|
|||||||
order?: string;
|
order?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
perPage?: number;
|
perPage?: number;
|
||||||
|
status?: string;
|
||||||
|
customer_id?: number;
|
||||||
}) =>
|
}) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: ["offers", "list", filters],
|
queryKey: ["offers", "list", filters],
|
||||||
@@ -80,6 +83,9 @@ export const offerListOptions = (filters: {
|
|||||||
if (filters.order) params.set("order", filters.order);
|
if (filters.order) params.set("order", filters.order);
|
||||||
if (filters.page) params.set("page", String(filters.page));
|
if (filters.page) params.set("page", String(filters.page));
|
||||||
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||||
|
if (filters.status) params.set("status", filters.status);
|
||||||
|
if (filters.customer_id)
|
||||||
|
params.set("customer_id", String(filters.customer_id));
|
||||||
const qs = params.toString();
|
const qs = params.toString();
|
||||||
return paginatedJsonQuery(`/api/admin/offers${qs ? `?${qs}` : ""}`);
|
return paginatedJsonQuery(`/api/admin/offers${qs ? `?${qs}` : ""}`);
|
||||||
},
|
},
|
||||||
@@ -127,9 +133,6 @@ export interface OfferDetailData {
|
|||||||
language: string;
|
language: string;
|
||||||
vat_rate: number;
|
vat_rate: number;
|
||||||
apply_vat: boolean;
|
apply_vat: boolean;
|
||||||
exchange_rate: string;
|
|
||||||
scope_title: string;
|
|
||||||
scope_description: string;
|
|
||||||
items?: OfferItemData[];
|
items?: OfferItemData[];
|
||||||
sections?: OfferSectionData[];
|
sections?: OfferSectionData[];
|
||||||
status: string;
|
status: string;
|
||||||
@@ -142,6 +145,9 @@ export const offerDetailOptions = (id: string | undefined) =>
|
|||||||
queryKey: ["offers", id],
|
queryKey: ["offers", id],
|
||||||
queryFn: () => jsonQuery<OfferDetailData>(`/api/admin/offers/${id}`),
|
queryFn: () => jsonQuery<OfferDetailData>(`/api/admin/offers/${id}`),
|
||||||
enabled: !!id,
|
enabled: !!id,
|
||||||
|
// 404s (deleted/missing offers) are not transient. Retrying just spams
|
||||||
|
// GETs and fires the detail-page redirect toast repeatedly.
|
||||||
|
retry: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const offerNextNumberOptions = () =>
|
export const offerNextNumberOptions = () =>
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ export interface ProjectData {
|
|||||||
project_notes?: ProjectNote[];
|
project_notes?: ProjectNote[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Project {
|
||||||
|
id: number;
|
||||||
|
project_number: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const projectListOptions = (filters: {
|
export const projectListOptions = (filters: {
|
||||||
search?: string;
|
search?: string;
|
||||||
sort?: string;
|
sort?: string;
|
||||||
@@ -45,7 +51,9 @@ export const projectListOptions = (filters: {
|
|||||||
if (filters.page) params.set("page", String(filters.page));
|
if (filters.page) params.set("page", String(filters.page));
|
||||||
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||||
const qs = params.toString();
|
const qs = params.toString();
|
||||||
return paginatedJsonQuery(`/api/admin/projects${qs ? `?${qs}` : ""}`);
|
return paginatedJsonQuery<Project>(
|
||||||
|
`/api/admin/projects${qs ? `?${qs}` : ""}`,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,12 @@ export interface CompanySettingsData {
|
|||||||
offer_number_pattern?: string;
|
offer_number_pattern?: string;
|
||||||
order_number_pattern?: string;
|
order_number_pattern?: string;
|
||||||
invoice_number_pattern?: string;
|
invoice_number_pattern?: string;
|
||||||
|
warehouse_receipt_prefix?: string;
|
||||||
|
warehouse_receipt_number_pattern?: string;
|
||||||
|
warehouse_issue_prefix?: string;
|
||||||
|
warehouse_issue_number_pattern?: string;
|
||||||
|
warehouse_inventory_prefix?: string;
|
||||||
|
warehouse_inventory_number_pattern?: string;
|
||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
app_version?: string;
|
app_version?: string;
|
||||||
|
|||||||
430
src/admin/lib/queries/warehouse.ts
Normal file
430
src/admin/lib/queries/warehouse.ts
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
import { queryOptions } from "@tanstack/react-query";
|
||||||
|
import { jsonQuery, paginatedJsonQuery } from "../apiAdapter";
|
||||||
|
|
||||||
|
// --- Types ---
|
||||||
|
|
||||||
|
export interface WarehouseItem {
|
||||||
|
id: number;
|
||||||
|
item_number: string | null;
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
category_id: number | null;
|
||||||
|
unit: string;
|
||||||
|
min_quantity: number | null;
|
||||||
|
is_active: boolean;
|
||||||
|
notes: string | null;
|
||||||
|
category?: { id: number; name: string } | null;
|
||||||
|
total_quantity?: number;
|
||||||
|
available_quantity?: number;
|
||||||
|
stock_value?: number;
|
||||||
|
below_minimum?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseReceipt {
|
||||||
|
id: number;
|
||||||
|
receipt_number: string | null;
|
||||||
|
supplier_id: number | null;
|
||||||
|
delivery_note_number: string | null;
|
||||||
|
delivery_note_date: string | null;
|
||||||
|
received_by: number | null;
|
||||||
|
notes: string | null;
|
||||||
|
status: "DRAFT" | "CONFIRMED" | "CANCELLED";
|
||||||
|
created_at: string;
|
||||||
|
modified_at: string | null;
|
||||||
|
supplier?: { id: number; name: string } | null;
|
||||||
|
received_by_user?: {
|
||||||
|
id: number;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
} | null;
|
||||||
|
_count?: { items: number };
|
||||||
|
items?: WarehouseReceiptItem[];
|
||||||
|
attachments?: WarehouseReceiptAttachment[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseReceiptItem {
|
||||||
|
id: number;
|
||||||
|
receipt_id: number;
|
||||||
|
item_id: number;
|
||||||
|
quantity: number;
|
||||||
|
unit_price: number;
|
||||||
|
location_id: number | null;
|
||||||
|
notes: string | null;
|
||||||
|
item?: WarehouseItem;
|
||||||
|
location?: { id: number; code: string; name: string } | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseReceiptAttachment {
|
||||||
|
id: number;
|
||||||
|
receipt_id: number;
|
||||||
|
file_name: string;
|
||||||
|
file_mime: string;
|
||||||
|
file_size: number;
|
||||||
|
file_path: string;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseIssue {
|
||||||
|
id: number;
|
||||||
|
issue_number: string | null;
|
||||||
|
project_id: number;
|
||||||
|
issued_by: number | null;
|
||||||
|
notes: string | null;
|
||||||
|
status: "DRAFT" | "CONFIRMED" | "CANCELLED";
|
||||||
|
created_at: string;
|
||||||
|
modified_at: string | null;
|
||||||
|
project?: { id: number; name: string; project_number: string } | null;
|
||||||
|
issued_by_user?: { id: number; first_name: string; last_name: string } | null;
|
||||||
|
_count?: { items: number };
|
||||||
|
items?: WarehouseIssueItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseIssueItem {
|
||||||
|
id: number;
|
||||||
|
issue_id: number;
|
||||||
|
item_id: number;
|
||||||
|
batch_id: number;
|
||||||
|
quantity: number;
|
||||||
|
location_id: number | null;
|
||||||
|
reservation_id: number | null;
|
||||||
|
notes: string | null;
|
||||||
|
item?: WarehouseItem;
|
||||||
|
batch?: {
|
||||||
|
id: number;
|
||||||
|
quantity: number;
|
||||||
|
unit_price: number;
|
||||||
|
received_at: string;
|
||||||
|
} | null;
|
||||||
|
location?: { id: number; code: string; name: string } | null;
|
||||||
|
reservation?: { id: number; quantity: number; remaining_qty: number } | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseReservation {
|
||||||
|
id: number;
|
||||||
|
item_id: number;
|
||||||
|
project_id: number;
|
||||||
|
quantity: number;
|
||||||
|
remaining_qty: number;
|
||||||
|
reserved_by: number | null;
|
||||||
|
notes: string | null;
|
||||||
|
status: "ACTIVE" | "FULFILLED" | "CANCELLED";
|
||||||
|
created_at: string;
|
||||||
|
modified_at: string | null;
|
||||||
|
item?: WarehouseItem;
|
||||||
|
project?: { id: number; name: string } | null;
|
||||||
|
reserved_by_user?: {
|
||||||
|
id: number;
|
||||||
|
first_name: string;
|
||||||
|
last_name: string;
|
||||||
|
} | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseInventorySession {
|
||||||
|
id: number;
|
||||||
|
session_number: string | null;
|
||||||
|
notes: string | null;
|
||||||
|
status: "DRAFT" | "CONFIRMED";
|
||||||
|
created_at: string;
|
||||||
|
modified_at: string | null;
|
||||||
|
items?: WarehouseInventoryItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseInventoryItem {
|
||||||
|
id: number;
|
||||||
|
session_id: number;
|
||||||
|
item_id: number;
|
||||||
|
location_id: number | null;
|
||||||
|
system_qty: number;
|
||||||
|
actual_qty: number;
|
||||||
|
difference: number;
|
||||||
|
notes: string | null;
|
||||||
|
item?: WarehouseItem;
|
||||||
|
location?: { id: number; code: string; name: string } | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseCategory {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
sort_order: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseLocation {
|
||||||
|
id: number;
|
||||||
|
code: string;
|
||||||
|
name: string;
|
||||||
|
description: string | null;
|
||||||
|
is_active: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WarehouseSupplier {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
ico: string | null;
|
||||||
|
dic: string | null;
|
||||||
|
contact_person: string | null;
|
||||||
|
email: string | null;
|
||||||
|
phone: string | null;
|
||||||
|
address: string | null;
|
||||||
|
notes: string | null;
|
||||||
|
is_active: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MovementLogRow {
|
||||||
|
date: string;
|
||||||
|
type: string;
|
||||||
|
document_number: string;
|
||||||
|
item_name: string;
|
||||||
|
quantity: number;
|
||||||
|
unit_price: number;
|
||||||
|
supplier_or_project: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Query Options ---
|
||||||
|
|
||||||
|
export const warehouseItemListOptions = (filters: {
|
||||||
|
search?: string;
|
||||||
|
page?: number;
|
||||||
|
perPage?: number;
|
||||||
|
sort?: string;
|
||||||
|
order?: string;
|
||||||
|
category_id?: number;
|
||||||
|
}) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "items", "list", filters],
|
||||||
|
queryFn: () => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (filters.search) params.set("search", filters.search);
|
||||||
|
if (filters.page) params.set("page", String(filters.page));
|
||||||
|
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||||
|
if (filters.sort) params.set("sort", filters.sort);
|
||||||
|
if (filters.order) params.set("order", filters.order);
|
||||||
|
if (filters.category_id)
|
||||||
|
params.set("category_id", String(filters.category_id));
|
||||||
|
const qs = params.toString();
|
||||||
|
return paginatedJsonQuery<WarehouseItem>(
|
||||||
|
`/api/admin/warehouse/items${qs ? `?${qs}` : ""}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseItemDetailOptions = (
|
||||||
|
id: string | undefined,
|
||||||
|
enabled?: boolean,
|
||||||
|
) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "items", id],
|
||||||
|
queryFn: () => jsonQuery<WarehouseItem>(`/api/admin/warehouse/items/${id}`),
|
||||||
|
enabled: enabled ?? !!id,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseCategoryListOptions = () =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "categories"],
|
||||||
|
queryFn: () =>
|
||||||
|
jsonQuery<WarehouseCategory[]>("/api/admin/warehouse/categories"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseSupplierListOptions = (filters: {
|
||||||
|
search?: string;
|
||||||
|
page?: number;
|
||||||
|
perPage?: number;
|
||||||
|
}) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "suppliers", "list", filters],
|
||||||
|
queryFn: () => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (filters.search) params.set("search", filters.search);
|
||||||
|
if (filters.page) params.set("page", String(filters.page));
|
||||||
|
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||||
|
const qs = params.toString();
|
||||||
|
return paginatedJsonQuery<WarehouseSupplier>(
|
||||||
|
`/api/admin/warehouse/suppliers${qs ? `?${qs}` : ""}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseLocationListOptions = (filters?: {
|
||||||
|
active?: "true" | "false" | "all";
|
||||||
|
}) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "locations", filters ?? { active: "true" }],
|
||||||
|
queryFn: () => {
|
||||||
|
const active = filters?.active ?? "true";
|
||||||
|
return jsonQuery<WarehouseLocation[]>(
|
||||||
|
`/api/admin/warehouse/locations?active=${active}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseReceiptListOptions = (filters: {
|
||||||
|
search?: string;
|
||||||
|
page?: number;
|
||||||
|
perPage?: number;
|
||||||
|
status?: string;
|
||||||
|
supplier_id?: number;
|
||||||
|
date_from?: string;
|
||||||
|
date_to?: string;
|
||||||
|
}) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "receipts", "list", filters],
|
||||||
|
queryFn: () => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (filters.search) params.set("search", filters.search);
|
||||||
|
if (filters.page) params.set("page", String(filters.page));
|
||||||
|
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||||
|
if (filters.status) params.set("status", filters.status);
|
||||||
|
if (filters.supplier_id)
|
||||||
|
params.set("supplier_id", String(filters.supplier_id));
|
||||||
|
if (filters.date_from) params.set("date_from", filters.date_from);
|
||||||
|
if (filters.date_to) params.set("date_to", filters.date_to);
|
||||||
|
const qs = params.toString();
|
||||||
|
return paginatedJsonQuery<WarehouseReceipt>(
|
||||||
|
`/api/admin/warehouse/receipts${qs ? `?${qs}` : ""}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseReceiptDetailOptions = (id: string | undefined) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "receipts", id],
|
||||||
|
queryFn: () =>
|
||||||
|
jsonQuery<WarehouseReceipt>(`/api/admin/warehouse/receipts/${id}`),
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseIssueListOptions = (filters: {
|
||||||
|
search?: string;
|
||||||
|
page?: number;
|
||||||
|
perPage?: number;
|
||||||
|
status?: string;
|
||||||
|
project_id?: number;
|
||||||
|
date_from?: string;
|
||||||
|
date_to?: string;
|
||||||
|
}) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "issues", "list", filters],
|
||||||
|
queryFn: () => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (filters.search) params.set("search", filters.search);
|
||||||
|
if (filters.page) params.set("page", String(filters.page));
|
||||||
|
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||||
|
if (filters.status) params.set("status", filters.status);
|
||||||
|
if (filters.project_id)
|
||||||
|
params.set("project_id", String(filters.project_id));
|
||||||
|
if (filters.date_from) params.set("date_from", filters.date_from);
|
||||||
|
if (filters.date_to) params.set("date_to", filters.date_to);
|
||||||
|
const qs = params.toString();
|
||||||
|
return paginatedJsonQuery<WarehouseIssue>(
|
||||||
|
`/api/admin/warehouse/issues${qs ? `?${qs}` : ""}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseIssueDetailOptions = (id: string | undefined) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "issues", id],
|
||||||
|
queryFn: () =>
|
||||||
|
jsonQuery<WarehouseIssue>(`/api/admin/warehouse/issues/${id}`),
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseReservationListOptions = (filters: {
|
||||||
|
page?: number;
|
||||||
|
perPage?: number;
|
||||||
|
item_id?: number;
|
||||||
|
project_id?: number;
|
||||||
|
status?: string;
|
||||||
|
}) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "reservations", "list", filters],
|
||||||
|
queryFn: () => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (filters.page) params.set("page", String(filters.page));
|
||||||
|
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||||
|
if (filters.item_id) params.set("item_id", String(filters.item_id));
|
||||||
|
if (filters.project_id)
|
||||||
|
params.set("project_id", String(filters.project_id));
|
||||||
|
if (filters.status) params.set("status", filters.status);
|
||||||
|
const qs = params.toString();
|
||||||
|
return paginatedJsonQuery<WarehouseReservation>(
|
||||||
|
`/api/admin/warehouse/reservations${qs ? `?${qs}` : ""}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseInventoryListOptions = (filters: {
|
||||||
|
page?: number;
|
||||||
|
perPage?: number;
|
||||||
|
status?: string;
|
||||||
|
}) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "inventory", "list", filters],
|
||||||
|
queryFn: () => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (filters.page) params.set("page", String(filters.page));
|
||||||
|
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||||
|
if (filters.status) params.set("status", filters.status);
|
||||||
|
const qs = params.toString();
|
||||||
|
return paginatedJsonQuery<WarehouseInventorySession>(
|
||||||
|
`/api/admin/warehouse/inventory-sessions${qs ? `?${qs}` : ""}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseInventoryDetailOptions = (id: string | undefined) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "inventory", id],
|
||||||
|
queryFn: () =>
|
||||||
|
jsonQuery<WarehouseInventorySession>(
|
||||||
|
`/api/admin/warehouse/inventory-sessions/${id}`,
|
||||||
|
),
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseStockStatusOptions = (filters?: {
|
||||||
|
category_id?: number;
|
||||||
|
}) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "stock-status", filters],
|
||||||
|
queryFn: () => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (filters?.category_id)
|
||||||
|
params.set("category_id", String(filters.category_id));
|
||||||
|
const qs = params.toString();
|
||||||
|
return jsonQuery<WarehouseItem[]>(
|
||||||
|
`/api/admin/warehouse/reports/stock-status${qs ? `?${qs}` : ""}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseBelowMinimumOptions = () =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "below-minimum"],
|
||||||
|
queryFn: () =>
|
||||||
|
jsonQuery<WarehouseItem[]>("/api/admin/warehouse/reports/below-minimum"),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const warehouseMovementLogOptions = (filters?: {
|
||||||
|
limit?: number;
|
||||||
|
type?: string;
|
||||||
|
project_id?: number;
|
||||||
|
date_from?: string;
|
||||||
|
date_to?: string;
|
||||||
|
}) =>
|
||||||
|
queryOptions({
|
||||||
|
queryKey: ["warehouse", "movement-log", filters],
|
||||||
|
queryFn: () => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (filters?.limit) params.set("limit", String(filters.limit));
|
||||||
|
if (filters?.type) params.set("type", filters.type);
|
||||||
|
if (filters?.project_id)
|
||||||
|
params.set("project_id", String(filters.project_id));
|
||||||
|
if (filters?.date_from) params.set("date_from", filters.date_from);
|
||||||
|
if (filters?.date_to) params.set("date_to", filters.date_to);
|
||||||
|
const qs = params.toString();
|
||||||
|
return jsonQuery<MovementLogRow[]>(
|
||||||
|
`/api/admin/warehouse/reports/movement-log${qs ? `?${qs}` : ""}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -46,33 +46,6 @@
|
|||||||
min-height: 32px;
|
min-height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Template dropdown menu */
|
|
||||||
.offers-template-menu {
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
right: 0;
|
|
||||||
z-index: 100;
|
|
||||||
min-width: 200px;
|
|
||||||
max-height: 250px;
|
|
||||||
overflow-y: auto;
|
|
||||||
background: var(--bg-primary);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.offers-template-menu-item {
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
transition: background var(--transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
.offers-template-menu-item:hover {
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Language badges */
|
/* Language badges */
|
||||||
.offers-lang-badge {
|
.offers-lang-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -617,13 +590,32 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Offer draft indicator */
|
/* Offer draft indicator */
|
||||||
.offers-draft-indicator {
|
/* Filters */
|
||||||
|
.admin-filters {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-wrap: wrap;
|
||||||
gap: 0.3rem;
|
gap: 1rem;
|
||||||
font-size: 0.72rem;
|
align-items: flex-start;
|
||||||
font-weight: 500;
|
}
|
||||||
color: var(--text-tertiary);
|
|
||||||
margin-top: 0.2rem;
|
.admin-filter-group {
|
||||||
opacity: 0.8;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-filter-label {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-filter-group .admin-tabs {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-filter-group .admin-form-select {
|
||||||
|
min-width: 10rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useAlert } from "../context/AlertContext";
|
import { useAlert } from "../context/AlertContext";
|
||||||
import { useAuth } from "../context/AuthContext";
|
import { useAuth } from "../context/AuthContext";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
@@ -16,8 +16,8 @@ import FormField from "../components/FormField";
|
|||||||
import Forbidden from "../components/Forbidden";
|
import Forbidden from "../components/Forbidden";
|
||||||
import apiFetch from "../utils/api";
|
import apiFetch from "../utils/api";
|
||||||
import { jsonQuery } from "../lib/apiAdapter";
|
import { jsonQuery } from "../lib/apiAdapter";
|
||||||
import { Skeleton } from "boneyard-js/react";
|
import { useApiMutation } from "../lib/queries/mutations";
|
||||||
import AttendanceFixture from "../fixtures/AttendanceFixture";
|
import { czechPlural } from "../utils/formatters";
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
@@ -65,7 +65,6 @@ interface MonthlyFund {
|
|||||||
leave_hours: number;
|
leave_hours: number;
|
||||||
vacation_hours: number;
|
vacation_hours: number;
|
||||||
sick_hours: number;
|
sick_hours: number;
|
||||||
holiday_hours: number;
|
|
||||||
unpaid_hours: number;
|
unpaid_hours: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,12 +78,6 @@ interface AttendanceData {
|
|||||||
active_project_id: number | null;
|
active_project_id: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pluralizeDays(n: number) {
|
|
||||||
if (n === 1) return "den";
|
|
||||||
if (n >= 2 && n <= 4) return "dny";
|
|
||||||
return "dnů";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFundBarBackground(fund: MonthlyFund) {
|
function getFundBarBackground(fund: MonthlyFund) {
|
||||||
if (fund.overtime > 0)
|
if (fund.overtime > 0)
|
||||||
return "linear-gradient(135deg, var(--warning), #d97706)";
|
return "linear-gradient(135deg, var(--warning), #d97706)";
|
||||||
@@ -96,7 +89,6 @@ function getFundBarBackground(fund: MonthlyFund) {
|
|||||||
export default function Attendance() {
|
export default function Attendance() {
|
||||||
const alert = useAlert();
|
const alert = useAlert();
|
||||||
const { hasPermission } = useAuth();
|
const { hasPermission } = useAuth();
|
||||||
const queryClient = useQueryClient();
|
|
||||||
|
|
||||||
const statusQuery = useQuery({
|
const statusQuery = useQuery({
|
||||||
queryKey: ["attendance", "status"],
|
queryKey: ["attendance", "status"],
|
||||||
@@ -109,6 +101,41 @@ export default function Attendance() {
|
|||||||
jsonQuery<Project[]>("/api/admin/attendance?action=projects"),
|
jsonQuery<Project[]>("/api/admin/attendance?action=projects"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const punchMutation = useApiMutation<
|
||||||
|
Record<string, unknown>,
|
||||||
|
{ message?: string }
|
||||||
|
>({
|
||||||
|
url: () => `${API_BASE}/attendance`,
|
||||||
|
method: () => "POST",
|
||||||
|
invalidate: ["attendance"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const notesMutation = useApiMutation<{ notes: string }, { message?: string }>(
|
||||||
|
{
|
||||||
|
url: () => `${API_BASE}/attendance/notes`,
|
||||||
|
method: () => "POST",
|
||||||
|
invalidate: ["attendance"],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const switchProjectMutation = useApiMutation<
|
||||||
|
{ project_id: number | null },
|
||||||
|
{ message?: string }
|
||||||
|
>({
|
||||||
|
url: () => `${API_BASE}/attendance/switch-project`,
|
||||||
|
method: () => "POST",
|
||||||
|
invalidate: ["attendance"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const leaveRequestMutation = useApiMutation<
|
||||||
|
{ leave_type: string; date_from: string; date_to: string; notes: string },
|
||||||
|
{ message?: string }
|
||||||
|
>({
|
||||||
|
url: () => `${API_BASE}/leave-requests`,
|
||||||
|
method: () => "POST",
|
||||||
|
invalidate: ["attendance", "leave-requests", "users"],
|
||||||
|
});
|
||||||
|
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
const [isLeaveModalOpen, setIsLeaveModalOpen] = useState(false);
|
const [isLeaveModalOpen, setIsLeaveModalOpen] = useState(false);
|
||||||
const [leaveForm, setLeaveForm] = useState({
|
const [leaveForm, setLeaveForm] = useState({
|
||||||
@@ -232,55 +259,29 @@ export default function Attendance() {
|
|||||||
gpsData: Record<string, unknown> = {},
|
gpsData: Record<string, unknown> = {},
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(`${API_BASE}/attendance`, {
|
const result = await punchMutation.mutateAsync({
|
||||||
method: "POST",
|
punch_action: action,
|
||||||
headers: { "Content-Type": "application/json" },
|
...gpsData,
|
||||||
body: JSON.stringify({ punch_action: action, ...gpsData }),
|
|
||||||
});
|
});
|
||||||
if (response.status === 401) return;
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
punchTimeoutRef.current = setTimeout(() => {
|
||||||
if (result.success) {
|
alert.success(result?.message || "Uloženo");
|
||||||
await queryClient.invalidateQueries({
|
}, 300);
|
||||||
queryKey: ["attendance"],
|
} catch (e) {
|
||||||
});
|
|
||||||
punchTimeoutRef.current = setTimeout(() => {
|
|
||||||
alert.success(result.data?.message || result.message || "Uloženo");
|
|
||||||
}, 300);
|
|
||||||
} else {
|
|
||||||
alert.error(result.error);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
alert.error("Chyba připojení");
|
alert.error(e instanceof Error ? e.message : "Chyba připojení");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleBreak = async () => {
|
const handleBreak = async () => {
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(`${API_BASE}/attendance`, {
|
const result = await punchMutation.mutateAsync({
|
||||||
method: "POST",
|
punch_action: "break_start",
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ punch_action: "break_start" }),
|
|
||||||
});
|
});
|
||||||
if (response.status === 401) return;
|
alert.success(result?.message || "Přestávka zaznamenána");
|
||||||
|
} catch (e) {
|
||||||
const result = await response.json();
|
alert.error(e instanceof Error ? e.message : "Chyba připojení");
|
||||||
if (result.success) {
|
|
||||||
await queryClient.invalidateQueries({
|
|
||||||
queryKey: ["attendance"],
|
|
||||||
});
|
|
||||||
alert.success(
|
|
||||||
result.data?.message || result.message || "Přestávka zaznamenána",
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
alert.error(result.error);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert.error("Chyba připojení");
|
|
||||||
} finally {
|
} finally {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
@@ -288,48 +289,22 @@ export default function Attendance() {
|
|||||||
|
|
||||||
const handleSaveNotes = async () => {
|
const handleSaveNotes = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(`${API_BASE}/attendance/notes`, {
|
await notesMutation.mutateAsync({ notes });
|
||||||
method: "POST",
|
alert.success("Poznámka byla uložena");
|
||||||
headers: { "Content-Type": "application/json" },
|
} catch (e) {
|
||||||
body: JSON.stringify({ notes }),
|
alert.error(e instanceof Error ? e.message : "Chyba připojení");
|
||||||
});
|
|
||||||
if (response.status === 401) return;
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
if (result.success) {
|
|
||||||
await queryClient.invalidateQueries({ queryKey: ["attendance"] });
|
|
||||||
alert.success("Poznámka byla uložena");
|
|
||||||
} else {
|
|
||||||
alert.error(result.error);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert.error("Chyba připojení");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSwitchProject = async (newProjectId: string | null) => {
|
const handleSwitchProject = async (newProjectId: string | null) => {
|
||||||
setSwitchingProject(true);
|
setSwitchingProject(true);
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(`${API_BASE}/attendance/switch-project`, {
|
const result = await switchProjectMutation.mutateAsync({
|
||||||
method: "POST",
|
project_id: newProjectId ? Number(newProjectId) : null,
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ project_id: newProjectId || null }),
|
|
||||||
});
|
});
|
||||||
if (response.status === 401) return;
|
alert.success(result?.message || "Projekt přepnut");
|
||||||
|
} catch (e) {
|
||||||
const result = await response.json();
|
alert.error(e instanceof Error ? e.message : "Chyba připojení");
|
||||||
if (result.success) {
|
|
||||||
await queryClient.invalidateQueries({
|
|
||||||
queryKey: ["attendance"],
|
|
||||||
});
|
|
||||||
alert.success(
|
|
||||||
result.data?.message || result.message || "Projekt přepnut",
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
alert.error(result.error);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert.error("Chyba připojení");
|
|
||||||
} finally {
|
} finally {
|
||||||
setSwitchingProject(false);
|
setSwitchingProject(false);
|
||||||
}
|
}
|
||||||
@@ -353,56 +328,33 @@ export default function Attendance() {
|
|||||||
const handleRequestSubmit = async () => {
|
const handleRequestSubmit = async () => {
|
||||||
setRequestSubmitting(true);
|
setRequestSubmitting(true);
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(`${API_BASE}/leave-requests`, {
|
const result = await leaveRequestMutation.mutateAsync(leaveForm);
|
||||||
method: "POST",
|
setIsLeaveModalOpen(false);
|
||||||
headers: { "Content-Type": "application/json" },
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
||||||
body: JSON.stringify(leaveForm),
|
alert.success(result?.message || "Žádost odeslána");
|
||||||
|
setLeaveForm({
|
||||||
|
leave_type: "vacation",
|
||||||
|
date_from: new Date().toISOString().split("T")[0],
|
||||||
|
date_to: new Date().toISOString().split("T")[0],
|
||||||
|
notes: "",
|
||||||
});
|
});
|
||||||
if (response.status === 401) return;
|
} catch (e) {
|
||||||
|
alert.error(e instanceof Error ? e.message : "Chyba připojení");
|
||||||
const result = await response.json();
|
|
||||||
if (result.success) {
|
|
||||||
setIsLeaveModalOpen(false);
|
|
||||||
await queryClient.invalidateQueries({
|
|
||||||
queryKey: ["attendance"],
|
|
||||||
});
|
|
||||||
await queryClient.invalidateQueries({
|
|
||||||
queryKey: ["leave-requests"],
|
|
||||||
});
|
|
||||||
await queryClient.invalidateQueries({
|
|
||||||
queryKey: ["leave"],
|
|
||||||
});
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
||||||
alert.success(
|
|
||||||
result.data?.message || result.message || "Žádost odeslána",
|
|
||||||
);
|
|
||||||
setLeaveForm({
|
|
||||||
leave_type: "vacation",
|
|
||||||
date_from: new Date().toISOString().split("T")[0],
|
|
||||||
date_to: new Date().toISOString().split("T")[0],
|
|
||||||
notes: "",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
alert.error(result.error);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert.error("Chyba připojení");
|
|
||||||
} finally {
|
} finally {
|
||||||
setRequestSubmitting(false);
|
setRequestSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!statusQuery.data) {
|
if (statusQuery.isPending) {
|
||||||
return (
|
return (
|
||||||
<Skeleton
|
<div className="admin-loading">
|
||||||
name="attendance"
|
<div className="admin-spinner" />
|
||||||
loading={statusQuery.isPending}
|
</div>
|
||||||
fixture={<AttendanceFixture />}
|
|
||||||
>
|
|
||||||
<div />
|
|
||||||
</Skeleton>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (!statusQuery.data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ongoing_shift: ongoingShift,
|
ongoing_shift: ongoingShift,
|
||||||
@@ -671,7 +623,13 @@ export default function Attendance() {
|
|||||||
{formatTime(shift.departure_time)}
|
{formatTime(shift.departure_time)}
|
||||||
</td>
|
</td>
|
||||||
<td className="admin-mono">
|
<td className="admin-mono">
|
||||||
{formatMinutes(calculateWorkMinutes(shift), true)}
|
{formatMinutes(
|
||||||
|
calculateWorkMinutes({
|
||||||
|
...shift,
|
||||||
|
notes: shift.notes ?? undefined,
|
||||||
|
}),
|
||||||
|
true,
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
{projects.length > 0 && (
|
{projects.length > 0 && (
|
||||||
<td>
|
<td>
|
||||||
@@ -741,7 +699,7 @@ export default function Attendance() {
|
|||||||
{vacationDaysRemaining}
|
{vacationDaysRemaining}
|
||||||
</span>
|
</span>
|
||||||
<span className="attendance-balance-unit">
|
<span className="attendance-balance-unit">
|
||||||
{pluralizeDays(vacationDaysRemaining)}
|
{czechPlural(vacationDaysRemaining, "den", "dny", "dnů")}
|
||||||
{vacationHoursRemaining > 0 && ` ${vacationHoursRemaining}h`}
|
{vacationHoursRemaining > 0 && ` ${vacationHoursRemaining}h`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -831,8 +789,6 @@ export default function Attendance() {
|
|||||||
` + dovolená ${data.monthly_fund.vacation_hours}h`}
|
` + dovolená ${data.monthly_fund.vacation_hours}h`}
|
||||||
{data.monthly_fund.sick_hours > 0 &&
|
{data.monthly_fund.sick_hours > 0 &&
|
||||||
` + nemoc ${data.monthly_fund.sick_hours}h`}
|
` + nemoc ${data.monthly_fund.sick_hours}h`}
|
||||||
{data.monthly_fund.holiday_hours > 0 &&
|
|
||||||
` + svátek ${data.monthly_fund.holiday_hours}h`}
|
|
||||||
{data.monthly_fund.unpaid_hours > 0 &&
|
{data.monthly_fund.unpaid_hours > 0 &&
|
||||||
` + neplacené ${data.monthly_fund.unpaid_hours}h`}
|
` + neplacené ${data.monthly_fund.unpaid_hours}h`}
|
||||||
)
|
)
|
||||||
@@ -1069,15 +1025,15 @@ export default function Attendance() {
|
|||||||
leaveForm.date_to,
|
leaveForm.date_to,
|
||||||
)}
|
)}
|
||||||
</strong>{" "}
|
</strong>{" "}
|
||||||
{(() => {
|
{czechPlural(
|
||||||
const d = calculateBusinessDays(
|
calculateBusinessDays(
|
||||||
leaveForm.date_from,
|
leaveForm.date_from,
|
||||||
leaveForm.date_to,
|
leaveForm.date_to,
|
||||||
);
|
),
|
||||||
if (d === 1) return "pracovní den";
|
"pracovní den",
|
||||||
if (d >= 2 && d <= 4) return "pracovní dny";
|
"pracovní dny",
|
||||||
return "pracovních dnů";
|
"pracovních dnů",
|
||||||
})()}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-muted">
|
<span className="text-muted">
|
||||||
{calculateBusinessDays(
|
{calculateBusinessDays(
|
||||||
|
|||||||
@@ -10,9 +10,7 @@ import AttendanceShiftTable from "../components/AttendanceShiftTable";
|
|||||||
import useModalLock from "../hooks/useModalLock";
|
import useModalLock from "../hooks/useModalLock";
|
||||||
import useAttendanceAdmin from "../hooks/useAttendanceAdmin";
|
import useAttendanceAdmin from "../hooks/useAttendanceAdmin";
|
||||||
import FormField from "../components/FormField";
|
import FormField from "../components/FormField";
|
||||||
import { formatMinutes } from "../utils/attendanceHelpers";
|
import { formatMinutes, formatHoursDecimal } from "../utils/attendanceHelpers";
|
||||||
import { Skeleton } from "boneyard-js/react";
|
|
||||||
import AttendanceAdminFixture from "../fixtures/AttendanceAdminFixture";
|
|
||||||
|
|
||||||
interface UserTotalData {
|
interface UserTotalData {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -20,20 +18,34 @@ interface UserTotalData {
|
|||||||
working: boolean;
|
working: boolean;
|
||||||
vacation_hours: number;
|
vacation_hours: number;
|
||||||
sick_hours: number;
|
sick_hours: number;
|
||||||
holiday_hours: number;
|
|
||||||
unpaid_hours: number;
|
unpaid_hours: number;
|
||||||
fund: number | null;
|
fund: number | null;
|
||||||
worked_hours: number;
|
worked_hours: number;
|
||||||
covered: number;
|
covered: number;
|
||||||
missing: number;
|
missing: number;
|
||||||
overtime: number;
|
overtime: number;
|
||||||
|
// mzda-style summary fields (set by computeUserTotals in useAttendanceAdmin)
|
||||||
|
odpracovano?: number;
|
||||||
|
vcetne_svatku?: number;
|
||||||
|
prescas?: number;
|
||||||
|
svatek?: number;
|
||||||
|
worked_holiday_hours?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFundBarBackground(data: UserTotalData) {
|
function getFundBarBackground(data: UserTotalData) {
|
||||||
if (data.overtime > 0)
|
// fondUsed mirrors the Fond "used" line in the IIFE below:
|
||||||
return "linear-gradient(135deg, var(--warning), #d97706)";
|
// real_worked + vacation + worked_holiday + free_holiday
|
||||||
if (data.covered >= (data.fund ?? 0))
|
// (delta is fondUsed - fund; 0 means exactly at the fund).
|
||||||
return "linear-gradient(135deg, var(--success), #059669)";
|
const realWorked = data.worked_hours_raw ?? data.worked_hours;
|
||||||
|
const fondUsed =
|
||||||
|
realWorked +
|
||||||
|
(data.vacation_hours ?? 0) +
|
||||||
|
(data.worked_holiday_hours ?? 0) +
|
||||||
|
(data.svatek ?? 0);
|
||||||
|
const fundVal = data.fund ?? 0;
|
||||||
|
const delta = fondUsed - fundVal;
|
||||||
|
if (delta > 0) return "linear-gradient(135deg, var(--warning), #d97706)";
|
||||||
|
if (delta >= 0) return "linear-gradient(135deg, var(--success), #059669)";
|
||||||
return "var(--gradient)";
|
return "var(--gradient)";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +101,7 @@ export default function AttendanceAdmin() {
|
|||||||
|
|
||||||
if (!hasPermission("attendance.manage")) return <Forbidden />;
|
if (!hasPermission("attendance.manage")) return <Forbidden />;
|
||||||
|
|
||||||
// Show skeleton only on initial load (no data yet), not on filter changes
|
// Show spinner only on initial load (no data yet), not on filter changes
|
||||||
const isInitialLoad =
|
const isInitialLoad =
|
||||||
loading &&
|
loading &&
|
||||||
data.records.length === 0 &&
|
data.records.length === 0 &&
|
||||||
@@ -97,13 +109,9 @@ export default function AttendanceAdmin() {
|
|||||||
|
|
||||||
if (isInitialLoad) {
|
if (isInitialLoad) {
|
||||||
return (
|
return (
|
||||||
<Skeleton
|
<div className="admin-loading">
|
||||||
name="attendance-admin"
|
<div className="admin-spinner" />
|
||||||
loading={isInitialLoad}
|
</div>
|
||||||
fixture={<AttendanceAdminFixture />}
|
|
||||||
>
|
|
||||||
<div />
|
|
||||||
</Skeleton>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,8 +220,15 @@ export default function AttendanceAdmin() {
|
|||||||
{Object.entries(data.user_totals).map(([uid, userData]) => {
|
{Object.entries(data.user_totals).map(([uid, userData]) => {
|
||||||
const ut = userData as UserTotalData;
|
const ut = userData as UserTotalData;
|
||||||
return (
|
return (
|
||||||
<div key={uid} className="admin-card">
|
<div key={uid} className="admin-card" style={{ display: "flex" }}>
|
||||||
<div className="admin-card-body">
|
<div
|
||||||
|
className="admin-card-body"
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
flex: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div className="flex-row gap-2 mb-2">
|
<div className="flex-row gap-2 mb-2">
|
||||||
<span style={{ fontWeight: 600 }}>{ut.name}</span>
|
<span style={{ fontWeight: 600 }}>{ut.name}</span>
|
||||||
<span
|
<span
|
||||||
@@ -229,9 +244,11 @@ export default function AttendanceAdmin() {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
marginTop: "0.5rem",
|
marginTop: "0.5rem",
|
||||||
|
marginBottom: "0.75rem",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
gap: "0.25rem",
|
gap: "0.4rem",
|
||||||
|
minHeight: "2rem",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{ut.vacation_hours > 0 && (
|
{ut.vacation_hours > 0 && (
|
||||||
@@ -244,9 +261,9 @@ export default function AttendanceAdmin() {
|
|||||||
Nem: {ut.sick_hours}h
|
Nem: {ut.sick_hours}h
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{ut.holiday_hours > 0 && (
|
{ut.svatek !== undefined && ut.svatek > 0 && (
|
||||||
<span className="attendance-leave-badge badge-holiday">
|
<span className="attendance-leave-badge badge-holiday">
|
||||||
Sv: {ut.holiday_hours}h
|
Sv: {Math.round(ut.svatek)}h
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{ut.unpaid_hours > 0 && (
|
{ut.unpaid_hours > 0 && (
|
||||||
@@ -255,62 +272,93 @@ export default function AttendanceAdmin() {
|
|||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{ut.fund !== null && (
|
{ut.fund !== null &&
|
||||||
<div className="mt-2">
|
(() => {
|
||||||
<div
|
// Fond "used" = real_worked + vacation + worked_holiday + free_holiday
|
||||||
className="text-secondary"
|
// (everything the user actually got paid for this month:
|
||||||
style={{
|
// raw worked time with sub-day remainders, plus
|
||||||
display: "flex",
|
// vacation days, plus hours worked on holidays, plus
|
||||||
justifyContent: "space-between",
|
// the 8h per unworked holiday from the fund).
|
||||||
alignItems: "center",
|
const realWorked = ut.worked_hours_raw ?? ut.worked_hours;
|
||||||
fontSize: "0.8rem",
|
const fondUsed =
|
||||||
}}
|
realWorked +
|
||||||
>
|
(ut.vacation_hours ?? 0) +
|
||||||
<span>
|
(ut.worked_holiday_hours ?? 0) +
|
||||||
Fond: {ut.worked_hours}h / {ut.fund}h
|
(ut.svatek ?? 0);
|
||||||
</span>
|
const fundVal = ut.fund ?? 0;
|
||||||
{ut.overtime > 0 && (
|
const delta = fondUsed - fundVal;
|
||||||
<span className="text-warning fw-600">
|
return (
|
||||||
+{ut.overtime}h
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
{ut.overtime <= 0 && ut.missing > 0 && (
|
|
||||||
<span className="text-danger fw-600">
|
|
||||||
-{ut.missing}h
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
marginTop: "0.375rem",
|
|
||||||
height: "4px",
|
|
||||||
background: "var(--bg-tertiary)",
|
|
||||||
borderRadius: "2px",
|
|
||||||
overflow: "hidden",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
|
className="kpi-card-footer"
|
||||||
style={{
|
style={{
|
||||||
height: "100%",
|
marginTop: "auto",
|
||||||
width: `${Math.min(100, (ut.covered / (ut.fund || 1)) * 100)}%`,
|
paddingTop: "0.75rem",
|
||||||
background: getFundBarBackground(ut),
|
borderTop: "1px solid var(--border-color)",
|
||||||
borderRadius: "2px",
|
|
||||||
transition: "width 0.3s ease",
|
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
</div>
|
<div
|
||||||
</div>
|
className="text-secondary"
|
||||||
)}
|
style={{
|
||||||
{data.leave_balances[uid] && (
|
display: "flex",
|
||||||
<div
|
justifyContent: "space-between",
|
||||||
className="text-secondary"
|
alignItems: "center",
|
||||||
style={{ marginTop: "0.5rem", fontSize: "0.8rem" }}
|
fontSize: "0.8rem",
|
||||||
>
|
}}
|
||||||
Zbývá dovolené:{" "}
|
>
|
||||||
{data.leave_balances[uid].vacation_remaining.toFixed(1)}h
|
<span>
|
||||||
/ {data.leave_balances[uid].vacation_total}h
|
Fond: {formatHoursDecimal(fondUsed * 60)}h /{" "}
|
||||||
</div>
|
{formatHoursDecimal(fundVal * 60)}h
|
||||||
)}
|
</span>
|
||||||
|
{delta > 0 && (
|
||||||
|
<span className="text-warning fw-600">
|
||||||
|
+{formatHoursDecimal(delta * 60)}h
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{delta <= 0 && delta < 0 && (
|
||||||
|
<span className="text-danger fw-600">
|
||||||
|
-{formatHoursDecimal(Math.abs(delta) * 60)}h
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: "0.375rem",
|
||||||
|
height: "4px",
|
||||||
|
background: "var(--bg-tertiary)",
|
||||||
|
borderRadius: "2px",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: "100%",
|
||||||
|
width: `${Math.min(
|
||||||
|
100,
|
||||||
|
(fondUsed / (ut.fund || 1)) * 100,
|
||||||
|
)}%`,
|
||||||
|
background: getFundBarBackground(ut),
|
||||||
|
borderRadius: "2px",
|
||||||
|
transition: "width 0.3s ease",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
<div
|
||||||
|
className="text-secondary"
|
||||||
|
style={{ marginTop: "0.75rem", fontSize: "0.8rem" }}
|
||||||
|
>
|
||||||
|
{data.leave_balances[uid] ? (
|
||||||
|
<>
|
||||||
|
Zbývá dovolené:{" "}
|
||||||
|
{data.leave_balances[uid].vacation_remaining.toFixed(1)}
|
||||||
|
h / {data.leave_balances[uid].vacation_total}h
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<span style={{ visibility: "hidden" }}>—</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -374,7 +422,14 @@ export default function AttendanceAdmin() {
|
|||||||
projectList={projectList}
|
projectList={projectList}
|
||||||
users={data.users}
|
users={data.users}
|
||||||
onShiftDateChange={handleCreateShiftDateChange}
|
onShiftDateChange={handleCreateShiftDateChange}
|
||||||
editingRecord={editingRecord}
|
editingRecord={
|
||||||
|
editingRecord
|
||||||
|
? {
|
||||||
|
user_name: editingRecord.user_name ?? "",
|
||||||
|
shift_date: editingRecord.shift_date,
|
||||||
|
}
|
||||||
|
: null
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { useState } from "react";
|
|||||||
import { useAlert } from "../context/AlertContext";
|
import { useAlert } from "../context/AlertContext";
|
||||||
import { useAuth } from "../context/AuthContext";
|
import { useAuth } from "../context/AuthContext";
|
||||||
import Forbidden from "../components/Forbidden";
|
import Forbidden from "../components/Forbidden";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import ConfirmModal from "../components/ConfirmModal";
|
import ConfirmModal from "../components/ConfirmModal";
|
||||||
import useModalLock from "../hooks/useModalLock";
|
import FormModal from "../components/FormModal";
|
||||||
import FormField from "../components/FormField";
|
import FormField from "../components/FormField";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
attendanceBalancesOptions,
|
attendanceBalancesOptions,
|
||||||
attendanceWorkFundOptions,
|
attendanceWorkFundOptions,
|
||||||
@@ -20,9 +20,8 @@ import {
|
|||||||
type UserShort,
|
type UserShort,
|
||||||
} from "../lib/queries/attendance";
|
} from "../lib/queries/attendance";
|
||||||
|
|
||||||
import apiFetch from "../utils/api";
|
import { useApiMutation } from "../lib/queries/mutations";
|
||||||
import { Skeleton } from "boneyard-js/react";
|
|
||||||
import AttendanceBalancesFixture from "../fixtures/AttendanceBalancesFixture";
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
const getVacationClass = (remaining: number): string => {
|
const getVacationClass = (remaining: number): string => {
|
||||||
@@ -85,7 +84,6 @@ const getProgressBackground = (
|
|||||||
export default function AttendanceBalances() {
|
export default function AttendanceBalances() {
|
||||||
const alert = useAlert();
|
const alert = useAlert();
|
||||||
const { hasPermission } = useAuth();
|
const { hasPermission } = useAuth();
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const [year, setYear] = useState(new Date().getFullYear());
|
const [year, setYear] = useState(new Date().getFullYear());
|
||||||
const { data: balancesData, isPending: balancesPending } = useQuery(
|
const { data: balancesData, isPending: balancesPending } = useQuery(
|
||||||
attendanceBalancesOptions(year),
|
attendanceBalancesOptions(year),
|
||||||
@@ -114,10 +112,37 @@ export default function AttendanceBalances() {
|
|||||||
userName: string;
|
userName: string;
|
||||||
}>({ show: false, userId: null, userName: "" });
|
}>({ show: false, userId: null, userName: "" });
|
||||||
|
|
||||||
useModalLock(showEditModal);
|
|
||||||
|
|
||||||
if (!hasPermission("attendance.balances")) return <Forbidden />;
|
if (!hasPermission("attendance.balances")) return <Forbidden />;
|
||||||
|
|
||||||
|
const editMutation = useApiMutation<
|
||||||
|
{
|
||||||
|
user_id: string;
|
||||||
|
year: number;
|
||||||
|
action_type: "edit";
|
||||||
|
vacation_total: number;
|
||||||
|
vacation_used: number;
|
||||||
|
sick_used: number;
|
||||||
|
},
|
||||||
|
{ message?: string; error?: string }
|
||||||
|
>({
|
||||||
|
url: () => `${API_BASE}/attendance?action=balances`,
|
||||||
|
method: () => "POST",
|
||||||
|
invalidate: ["attendance", "users"],
|
||||||
|
});
|
||||||
|
|
||||||
|
const resetMutation = useApiMutation<
|
||||||
|
{ user_id: string; year: number; action_type: "reset" },
|
||||||
|
{ message?: string; error?: string }
|
||||||
|
>({
|
||||||
|
url: () => `${API_BASE}/attendance?action=balances`,
|
||||||
|
method: () => "POST",
|
||||||
|
invalidate: ["attendance", "users"],
|
||||||
|
onSuccess: (data) => {
|
||||||
|
setResetConfirm({ show: false, userId: null, userName: "" });
|
||||||
|
alert.success(data?.message || "Operace dokončena");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const openEditModal = (userId: string, balance: BalanceEntry) => {
|
const openEditModal = (userId: string, balance: BalanceEntry) => {
|
||||||
setEditingUser({ id: userId, name: balance.name });
|
setEditingUser({ id: userId, name: balance.name });
|
||||||
setEditForm({
|
setEditForm({
|
||||||
@@ -131,62 +156,29 @@ export default function AttendanceBalances() {
|
|||||||
const handleEditSubmit = async () => {
|
const handleEditSubmit = async () => {
|
||||||
if (!editingUser) return;
|
if (!editingUser) return;
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(
|
const result = await editMutation.mutateAsync({
|
||||||
`${API_BASE}/attendance?action=balances`,
|
user_id: editingUser.id,
|
||||||
{
|
year,
|
||||||
method: "POST",
|
action_type: "edit",
|
||||||
headers: { "Content-Type": "application/json" },
|
...editForm,
|
||||||
body: JSON.stringify({
|
});
|
||||||
user_id: editingUser.id,
|
setShowEditModal(false);
|
||||||
year,
|
alert.success(result?.message || "Upraveno");
|
||||||
action_type: "edit",
|
} catch (e) {
|
||||||
...editForm,
|
alert.error(e instanceof Error ? e.message : "Chyba připojení");
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
setShowEditModal(false);
|
|
||||||
await queryClient.invalidateQueries({ queryKey: ["attendance"] });
|
|
||||||
alert.success(result.message);
|
|
||||||
} else {
|
|
||||||
alert.error(result.error);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert.error("Chyba připojení");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = async () => {
|
const handleReset = async () => {
|
||||||
if (!resetConfirm.userId) return;
|
if (!resetConfirm.userId) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(
|
await resetMutation.mutateAsync({
|
||||||
`${API_BASE}/attendance?action=balances`,
|
user_id: resetConfirm.userId,
|
||||||
{
|
year,
|
||||||
method: "POST",
|
action_type: "reset",
|
||||||
headers: { "Content-Type": "application/json" },
|
});
|
||||||
body: JSON.stringify({
|
} catch (e) {
|
||||||
user_id: resetConfirm.userId,
|
alert.error(e instanceof Error ? e.message : "Chyba připojení");
|
||||||
year,
|
|
||||||
action_type: "reset",
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
setResetConfirm({ show: false, userId: null, userName: "" });
|
|
||||||
await queryClient.invalidateQueries({ queryKey: ["attendance"] });
|
|
||||||
alert.success(result.message);
|
|
||||||
} else {
|
|
||||||
alert.error(result.error);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert.error("Chyba připojení");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -263,11 +255,11 @@ export default function AttendanceBalances() {
|
|||||||
transition={{ duration: 0.25, delay: 0.06 }}
|
transition={{ duration: 0.25, delay: 0.06 }}
|
||||||
>
|
>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<Skeleton
|
{balancesPending ? (
|
||||||
name="attendance-balances"
|
<div className="admin-loading">
|
||||||
loading={balancesPending}
|
<div className="admin-spinner" />
|
||||||
fixture={<AttendanceBalancesFixture />}
|
</div>
|
||||||
>
|
) : (
|
||||||
<>
|
<>
|
||||||
{balancesData &&
|
{balancesData &&
|
||||||
Object.keys(balancesData.balances).length === 0 && (
|
Object.keys(balancesData.balances).length === 0 && (
|
||||||
@@ -387,7 +379,7 @@ export default function AttendanceBalances() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
</Skeleton>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
@@ -557,13 +549,9 @@ export default function AttendanceBalances() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{fundPending && (
|
{fundPending && (
|
||||||
<Skeleton
|
<div className="admin-loading">
|
||||||
name="attendance-balances-fund"
|
<div className="admin-spinner" />
|
||||||
loading={fundPending}
|
</div>
|
||||||
fixture={<AttendanceBalancesFixture />}
|
|
||||||
>
|
|
||||||
<div className="mt-6" />
|
|
||||||
</Skeleton>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Monthly Project Overview */}
|
{/* Monthly Project Overview */}
|
||||||
@@ -761,118 +749,82 @@ export default function AttendanceBalances() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{projectPending && (
|
{projectPending && (
|
||||||
<Skeleton
|
<div className="admin-loading">
|
||||||
name="attendance-balances-projects"
|
<div className="admin-spinner" />
|
||||||
loading={projectPending}
|
</div>
|
||||||
fixture={<AttendanceBalancesFixture />}
|
|
||||||
>
|
|
||||||
<div className="mt-6" />
|
|
||||||
</Skeleton>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Edit Modal */}
|
{/* Edit Modal */}
|
||||||
<AnimatePresence>
|
<FormModal
|
||||||
{showEditModal && editingUser && (
|
isOpen={showEditModal && !!editingUser}
|
||||||
<motion.div
|
onClose={() => setShowEditModal(false)}
|
||||||
className="admin-modal-overlay"
|
onSubmit={handleEditSubmit}
|
||||||
initial={{ opacity: 0 }}
|
title="Upravit dovolenou"
|
||||||
animate={{ opacity: 1 }}
|
loading={editMutation.isPending}
|
||||||
exit={{ opacity: 0 }}
|
>
|
||||||
transition={{ duration: 0.2 }}
|
{editingUser && (
|
||||||
>
|
<>
|
||||||
<div
|
<p
|
||||||
className="admin-modal-backdrop"
|
className="text-secondary"
|
||||||
onClick={() => setShowEditModal(false)}
|
style={{ marginTop: "0", marginBottom: "0.75rem" }}
|
||||||
/>
|
|
||||||
<motion.div
|
|
||||||
className="admin-modal"
|
|
||||||
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
|
||||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
||||||
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
|
||||||
transition={{ duration: 0.2 }}
|
|
||||||
>
|
>
|
||||||
<div className="admin-modal-header">
|
{editingUser.name}
|
||||||
<h2 className="admin-modal-title">Upravit dovolenou</h2>
|
</p>
|
||||||
<p className="text-secondary" style={{ marginTop: "0.25rem" }}>
|
<div className="admin-form">
|
||||||
{editingUser.name}
|
<FormField label="Nárok na dovolenou (hodiny)">
|
||||||
</p>
|
<input
|
||||||
</div>
|
type="number"
|
||||||
|
value={editForm.vacation_total}
|
||||||
|
onChange={(e) =>
|
||||||
|
setEditForm({
|
||||||
|
...editForm,
|
||||||
|
vacation_total: parseFloat(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
min="0"
|
||||||
|
max="500"
|
||||||
|
step="1"
|
||||||
|
className="admin-form-input"
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
<div className="admin-modal-body">
|
<FormField label="Čerpáno dovolené (hodiny)">
|
||||||
<div className="admin-form">
|
<input
|
||||||
<FormField label="Nárok na dovolenou (hodiny)">
|
type="number"
|
||||||
<input
|
value={editForm.vacation_used}
|
||||||
type="number"
|
onChange={(e) =>
|
||||||
value={editForm.vacation_total}
|
setEditForm({
|
||||||
onChange={(e) =>
|
...editForm,
|
||||||
setEditForm({
|
vacation_used: parseFloat(e.target.value),
|
||||||
...editForm,
|
})
|
||||||
vacation_total: parseFloat(e.target.value),
|
}
|
||||||
})
|
min="0"
|
||||||
}
|
max="500"
|
||||||
min="0"
|
step="0.5"
|
||||||
max="500"
|
className="admin-form-input"
|
||||||
step="1"
|
/>
|
||||||
className="admin-form-input"
|
</FormField>
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
|
|
||||||
<FormField label="Čerpáno dovolené (hodiny)">
|
<FormField label="Čerpáno nemocenské (hodiny)">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
value={editForm.vacation_used}
|
value={editForm.sick_used}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setEditForm({
|
setEditForm({
|
||||||
...editForm,
|
...editForm,
|
||||||
vacation_used: parseFloat(e.target.value),
|
sick_used: parseFloat(e.target.value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
min="0"
|
min="0"
|
||||||
max="500"
|
max="500"
|
||||||
step="0.5"
|
step="0.5"
|
||||||
className="admin-form-input"
|
className="admin-form-input"
|
||||||
/>
|
/>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
</div>
|
||||||
<FormField label="Čerpáno nemocenské (hodiny)">
|
</>
|
||||||
<input
|
|
||||||
type="number"
|
|
||||||
value={editForm.sick_used}
|
|
||||||
onChange={(e) =>
|
|
||||||
setEditForm({
|
|
||||||
...editForm,
|
|
||||||
sick_used: parseFloat(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
min="0"
|
|
||||||
max="500"
|
|
||||||
step="0.5"
|
|
||||||
className="admin-form-input"
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="admin-modal-footer">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShowEditModal(false)}
|
|
||||||
className="admin-btn admin-btn-secondary"
|
|
||||||
>
|
|
||||||
Zrušit
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={handleEditSubmit}
|
|
||||||
className="admin-btn admin-btn-primary"
|
|
||||||
>
|
|
||||||
Uložit
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
</motion.div>
|
|
||||||
)}
|
)}
|
||||||
</AnimatePresence>
|
</FormModal>
|
||||||
|
|
||||||
{/* Reset Confirmation */}
|
{/* Reset Confirmation */}
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
@@ -885,6 +837,7 @@ export default function AttendanceBalances() {
|
|||||||
message={`Opravdu chcete vynulovat čerpání dovolené a nemocenské pro ${resetConfirm.userName} za rok ${year}?`}
|
message={`Opravdu chcete vynulovat čerpání dovolené a nemocenské pro ${resetConfirm.userName} za rok ${year}?`}
|
||||||
confirmText="Resetovat"
|
confirmText="Resetovat"
|
||||||
confirmVariant="danger"
|
confirmVariant="danger"
|
||||||
|
loading={resetMutation.isPending}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { userListOptions } from "../lib/queries/users";
|
import { userListOptions } from "../lib/queries/users";
|
||||||
import { useAlert } from "../context/AlertContext";
|
import { useAlert } from "../context/AlertContext";
|
||||||
import { useAuth } from "../context/AuthContext";
|
import { useAuth } from "../context/AuthContext";
|
||||||
@@ -9,9 +9,7 @@ import { motion } from "framer-motion";
|
|||||||
|
|
||||||
import AdminDatePicker from "../components/AdminDatePicker";
|
import AdminDatePicker from "../components/AdminDatePicker";
|
||||||
import FormField from "../components/FormField";
|
import FormField from "../components/FormField";
|
||||||
import apiFetch from "../utils/api";
|
import { useApiMutation } from "../lib/queries/mutations";
|
||||||
import { Skeleton } from "boneyard-js/react";
|
|
||||||
import AttendanceCreateFixture from "../fixtures/AttendanceCreateFixture";
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
interface CreateForm {
|
interface CreateForm {
|
||||||
@@ -34,7 +32,6 @@ export default function AttendanceCreate() {
|
|||||||
const alert = useAlert();
|
const alert = useAlert();
|
||||||
const { hasPermission } = useAuth();
|
const { hasPermission } = useAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const { data: usersData, isPending: loading } = useQuery(userListOptions());
|
const { data: usersData, isPending: loading } = useQuery(userListOptions());
|
||||||
const users = (usersData ?? []).map((u) => ({
|
const users = (usersData ?? []).map((u) => ({
|
||||||
id: u.id,
|
id: u.id,
|
||||||
@@ -42,6 +39,12 @@ export default function AttendanceCreate() {
|
|||||||
}));
|
}));
|
||||||
const [submitting, setSubmitting] = useState(false);
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
|
||||||
|
const createMutation = useApiMutation<CreateForm, { message?: string }>({
|
||||||
|
url: () => `${API_BASE}/attendance`,
|
||||||
|
method: () => "POST",
|
||||||
|
invalidate: ["attendance", "users"],
|
||||||
|
});
|
||||||
|
|
||||||
const [form, setForm] = useState<CreateForm>(() => {
|
const [form, setForm] = useState<CreateForm>(() => {
|
||||||
const today = new Date().toISOString().split("T")[0];
|
const today = new Date().toISOString().split("T")[0];
|
||||||
return {
|
return {
|
||||||
@@ -72,23 +75,11 @@ export default function AttendanceCreate() {
|
|||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(`${API_BASE}/attendance`, {
|
const result = await createMutation.mutateAsync(form);
|
||||||
method: "POST",
|
alert.success(result?.message || "Uloženo");
|
||||||
headers: { "Content-Type": "application/json" },
|
navigate(`/attendance/admin?month=${form.shift_date.substring(0, 7)}`);
|
||||||
body: JSON.stringify(form),
|
} catch (e) {
|
||||||
});
|
alert.error(e instanceof Error ? e.message : "Chyba připojení");
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
alert.success(result.message);
|
|
||||||
await queryClient.invalidateQueries({ queryKey: ["attendance"] });
|
|
||||||
navigate(`/attendance/admin?month=${form.shift_date.substring(0, 7)}`);
|
|
||||||
} else {
|
|
||||||
alert.error(result.error);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert.error("Chyba připojení");
|
|
||||||
} finally {
|
} finally {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
@@ -109,223 +100,224 @@ export default function AttendanceCreate() {
|
|||||||
|
|
||||||
if (!hasPermission("attendance.manage")) return <Forbidden />;
|
if (!hasPermission("attendance.manage")) return <Forbidden />;
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="admin-loading">
|
||||||
|
<div className="admin-spinner" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Skeleton
|
<div>
|
||||||
name="attendance-create"
|
<motion.div
|
||||||
loading={loading}
|
className="admin-page-header"
|
||||||
fixture={<AttendanceCreateFixture />}
|
initial={{ opacity: 0, y: 12 }}
|
||||||
>
|
animate={{ opacity: 1, y: 0 }}
|
||||||
<div>
|
transition={{ duration: 0.25 }}
|
||||||
<motion.div
|
>
|
||||||
className="admin-page-header"
|
<div>
|
||||||
initial={{ opacity: 0, y: 12 }}
|
<h1 className="admin-page-title">Přidat záznam docházky</h1>
|
||||||
animate={{ opacity: 1, y: 0 }}
|
</div>
|
||||||
transition={{ duration: 0.25 }}
|
<div className="admin-page-actions">
|
||||||
>
|
<Link
|
||||||
<div>
|
to="/attendance/admin"
|
||||||
<h1 className="admin-page-title">Přidat záznam docházky</h1>
|
className="admin-btn admin-btn-secondary"
|
||||||
</div>
|
>
|
||||||
<div className="admin-page-actions">
|
← Zpět na správu
|
||||||
<Link
|
</Link>
|
||||||
to="/attendance/admin"
|
</div>
|
||||||
className="admin-btn admin-btn-secondary"
|
</motion.div>
|
||||||
>
|
|
||||||
← Zpět na správu
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
className="admin-card"
|
className="admin-card"
|
||||||
style={{ maxWidth: "600px" }}
|
style={{ maxWidth: "600px" }}
|
||||||
initial={{ opacity: 0, y: 12 }}
|
initial={{ opacity: 0, y: 12 }}
|
||||||
animate={{ opacity: 1, y: 0 }}
|
animate={{ opacity: 1, y: 0 }}
|
||||||
transition={{ duration: 0.25, delay: 0.06 }}
|
transition={{ duration: 0.25, delay: 0.06 }}
|
||||||
>
|
>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<form onSubmit={handleSubmit} className="admin-form">
|
<form onSubmit={handleSubmit} className="admin-form">
|
||||||
<div className="admin-form-row">
|
<div className="admin-form-row">
|
||||||
<FormField label="Zaměstnanec" required>
|
<FormField label="Zaměstnanec" required>
|
||||||
<select
|
|
||||||
value={form.user_id}
|
|
||||||
onChange={(e) =>
|
|
||||||
setForm({ ...form, user_id: e.target.value })
|
|
||||||
}
|
|
||||||
className="admin-form-select"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<option value="">Vyberte zaměstnance</option>
|
|
||||||
{users.map((user) => (
|
|
||||||
<option key={user.id} value={user.id}>
|
|
||||||
{user.name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</FormField>
|
|
||||||
<FormField label="Datum směny" required>
|
|
||||||
<AdminDatePicker
|
|
||||||
mode="date"
|
|
||||||
value={form.shift_date}
|
|
||||||
onChange={(val: string) => handleShiftDateChange(val)}
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<FormField label="Typ záznamu" required>
|
|
||||||
<select
|
<select
|
||||||
value={form.leave_type}
|
value={form.user_id}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
setForm({ ...form, leave_type: e.target.value })
|
setForm({ ...form, user_id: e.target.value })
|
||||||
}
|
}
|
||||||
className="admin-form-select"
|
className="admin-form-select"
|
||||||
|
required
|
||||||
>
|
>
|
||||||
<option value="work">Práce</option>
|
<option value="">Vyberte zaměstnance</option>
|
||||||
<option value="vacation">Dovolená</option>
|
{users.map((user) => (
|
||||||
<option value="sick">Nemoc</option>
|
<option key={user.id} value={user.id}>
|
||||||
<option value="holiday">Svátek</option>
|
{user.name}
|
||||||
<option value="unpaid">Neplacené volno</option>
|
</option>
|
||||||
|
))}
|
||||||
</select>
|
</select>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
<FormField label="Datum směny" required>
|
||||||
{!isWorkType && (
|
<AdminDatePicker
|
||||||
<FormField label="Počet hodin">
|
mode="date"
|
||||||
<input
|
value={form.shift_date}
|
||||||
type="number"
|
onChange={(val: string) => handleShiftDateChange(val)}
|
||||||
value={form.leave_hours}
|
required
|
||||||
onChange={(e) =>
|
|
||||||
setForm({
|
|
||||||
...form,
|
|
||||||
leave_hours: parseFloat(e.target.value),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
min="0.5"
|
|
||||||
max="24"
|
|
||||||
step="0.5"
|
|
||||||
className="admin-form-input"
|
|
||||||
/>
|
|
||||||
<small className="admin-form-hint">
|
|
||||||
Výchozí 8 hodin pro celý den
|
|
||||||
</small>
|
|
||||||
</FormField>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isWorkType && (
|
|
||||||
<>
|
|
||||||
<div className="admin-form-row">
|
|
||||||
<FormField label="Příchod - datum">
|
|
||||||
<AdminDatePicker
|
|
||||||
mode="date"
|
|
||||||
value={form.arrival_date}
|
|
||||||
onChange={(val: string) =>
|
|
||||||
setForm({ ...form, arrival_date: val })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
<FormField label="Příchod - čas">
|
|
||||||
<AdminDatePicker
|
|
||||||
mode="time"
|
|
||||||
value={form.arrival_time}
|
|
||||||
onChange={(val: string) =>
|
|
||||||
setForm({ ...form, arrival_time: val })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="admin-form-row">
|
|
||||||
<FormField label="Začátek pauzy - datum">
|
|
||||||
<AdminDatePicker
|
|
||||||
mode="date"
|
|
||||||
value={form.break_start_date}
|
|
||||||
onChange={(val: string) =>
|
|
||||||
setForm({ ...form, break_start_date: val })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
<FormField label="Začátek pauzy - čas">
|
|
||||||
<AdminDatePicker
|
|
||||||
mode="time"
|
|
||||||
value={form.break_start_time}
|
|
||||||
onChange={(val: string) =>
|
|
||||||
setForm({ ...form, break_start_time: val })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="admin-form-row">
|
|
||||||
<FormField label="Konec pauzy - datum">
|
|
||||||
<AdminDatePicker
|
|
||||||
mode="date"
|
|
||||||
value={form.break_end_date}
|
|
||||||
onChange={(val: string) =>
|
|
||||||
setForm({ ...form, break_end_date: val })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
<FormField label="Konec pauzy - čas">
|
|
||||||
<AdminDatePicker
|
|
||||||
mode="time"
|
|
||||||
value={form.break_end_time}
|
|
||||||
onChange={(val: string) =>
|
|
||||||
setForm({ ...form, break_end_time: val })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="admin-form-row">
|
|
||||||
<FormField label="Odchod - datum">
|
|
||||||
<AdminDatePicker
|
|
||||||
mode="date"
|
|
||||||
value={form.departure_date}
|
|
||||||
onChange={(val: string) =>
|
|
||||||
setForm({ ...form, departure_date: val })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
<FormField label="Odchod - čas">
|
|
||||||
<AdminDatePicker
|
|
||||||
mode="time"
|
|
||||||
value={form.departure_time}
|
|
||||||
onChange={(val: string) =>
|
|
||||||
setForm({ ...form, departure_time: val })
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<FormField label="Poznámka">
|
|
||||||
<textarea
|
|
||||||
value={form.notes}
|
|
||||||
onChange={(e) => setForm({ ...form, notes: e.target.value })}
|
|
||||||
className="admin-form-textarea"
|
|
||||||
rows={3}
|
|
||||||
/>
|
/>
|
||||||
</FormField>
|
</FormField>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="admin-form-actions">
|
<FormField label="Typ záznamu" required>
|
||||||
<Link
|
<select
|
||||||
to="/attendance/admin"
|
value={form.leave_type}
|
||||||
className="admin-btn admin-btn-secondary"
|
onChange={(e) =>
|
||||||
>
|
setForm({ ...form, leave_type: e.target.value })
|
||||||
Zrušit
|
}
|
||||||
</Link>
|
className="admin-form-select"
|
||||||
<button
|
>
|
||||||
type="submit"
|
<option value="work">Práce</option>
|
||||||
disabled={submitting}
|
<option value="vacation">Dovolená</option>
|
||||||
className="admin-btn admin-btn-primary"
|
<option value="sick">Nemoc</option>
|
||||||
>
|
<option value="unpaid">Neplacené volno</option>
|
||||||
{submitting ? "Ukládám..." : "Uložit"}
|
</select>
|
||||||
</button>
|
</FormField>
|
||||||
</div>
|
|
||||||
</form>
|
{!isWorkType && (
|
||||||
</div>
|
<FormField label="Počet hodin">
|
||||||
</motion.div>
|
<input
|
||||||
</div>
|
type="number"
|
||||||
</Skeleton>
|
value={form.leave_hours}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm({
|
||||||
|
...form,
|
||||||
|
leave_hours: parseFloat(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
min="0.5"
|
||||||
|
max="24"
|
||||||
|
step="0.5"
|
||||||
|
className="admin-form-input"
|
||||||
|
/>
|
||||||
|
<small className="admin-form-hint">
|
||||||
|
Výchozí 8 hodin pro celý den
|
||||||
|
</small>
|
||||||
|
</FormField>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isWorkType && (
|
||||||
|
<>
|
||||||
|
<div className="admin-form-row">
|
||||||
|
<FormField label="Příchod - datum">
|
||||||
|
<AdminDatePicker
|
||||||
|
mode="date"
|
||||||
|
value={form.arrival_date}
|
||||||
|
onChange={(val: string) =>
|
||||||
|
setForm({ ...form, arrival_date: val })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
<FormField label="Příchod - čas">
|
||||||
|
<AdminDatePicker
|
||||||
|
mode="time"
|
||||||
|
value={form.arrival_time}
|
||||||
|
onChange={(val: string) =>
|
||||||
|
setForm({ ...form, arrival_time: val })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="admin-form-row">
|
||||||
|
<FormField label="Začátek pauzy - datum">
|
||||||
|
<AdminDatePicker
|
||||||
|
mode="date"
|
||||||
|
value={form.break_start_date}
|
||||||
|
onChange={(val: string) =>
|
||||||
|
setForm({ ...form, break_start_date: val })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
<FormField label="Začátek pauzy - čas">
|
||||||
|
<AdminDatePicker
|
||||||
|
mode="time"
|
||||||
|
value={form.break_start_time}
|
||||||
|
onChange={(val: string) =>
|
||||||
|
setForm({ ...form, break_start_time: val })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="admin-form-row">
|
||||||
|
<FormField label="Konec pauzy - datum">
|
||||||
|
<AdminDatePicker
|
||||||
|
mode="date"
|
||||||
|
value={form.break_end_date}
|
||||||
|
onChange={(val: string) =>
|
||||||
|
setForm({ ...form, break_end_date: val })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
<FormField label="Konec pauzy - čas">
|
||||||
|
<AdminDatePicker
|
||||||
|
mode="time"
|
||||||
|
value={form.break_end_time}
|
||||||
|
onChange={(val: string) =>
|
||||||
|
setForm({ ...form, break_end_time: val })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="admin-form-row">
|
||||||
|
<FormField label="Odchod - datum">
|
||||||
|
<AdminDatePicker
|
||||||
|
mode="date"
|
||||||
|
value={form.departure_date}
|
||||||
|
onChange={(val: string) =>
|
||||||
|
setForm({ ...form, departure_date: val })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
<FormField label="Odchod - čas">
|
||||||
|
<AdminDatePicker
|
||||||
|
mode="time"
|
||||||
|
value={form.departure_time}
|
||||||
|
onChange={(val: string) =>
|
||||||
|
setForm({ ...form, departure_time: val })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<FormField label="Poznámka">
|
||||||
|
<textarea
|
||||||
|
value={form.notes}
|
||||||
|
onChange={(e) => setForm({ ...form, notes: e.target.value })}
|
||||||
|
className="admin-form-textarea"
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</FormField>
|
||||||
|
|
||||||
|
<div className="admin-form-actions">
|
||||||
|
<Link
|
||||||
|
to="/attendance/admin"
|
||||||
|
className="admin-btn admin-btn-secondary"
|
||||||
|
>
|
||||||
|
Zrušit
|
||||||
|
</Link>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={submitting}
|
||||||
|
className="admin-btn admin-btn-primary"
|
||||||
|
>
|
||||||
|
{submitting ? "Ukládám..." : "Uložit"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,12 @@ import {
|
|||||||
getLeaveTypeBadgeClass,
|
getLeaveTypeBadgeClass,
|
||||||
calculateWorkMinutesPrint,
|
calculateWorkMinutesPrint,
|
||||||
formatTimeOrDatetimePrint,
|
formatTimeOrDatetimePrint,
|
||||||
|
calculateFreeHolidayHours,
|
||||||
|
holidaysInMonth,
|
||||||
|
normalizeDateStr,
|
||||||
|
formatHoursDecimal,
|
||||||
} from "../utils/attendanceHelpers";
|
} from "../utils/attendanceHelpers";
|
||||||
import FormField from "../components/FormField";
|
import FormField from "../components/FormField";
|
||||||
import { Skeleton } from "boneyard-js/react";
|
|
||||||
import AttendanceHistoryFixture from "../fixtures/AttendanceHistoryFixture";
|
|
||||||
|
|
||||||
const MONTH_NAMES = [
|
const MONTH_NAMES = [
|
||||||
"Leden",
|
"Leden",
|
||||||
@@ -196,7 +198,6 @@ export default function AttendanceHistory() {
|
|||||||
let totalMinutes = 0;
|
let totalMinutes = 0;
|
||||||
let vacationHours = 0;
|
let vacationHours = 0;
|
||||||
let sickHours = 0;
|
let sickHours = 0;
|
||||||
let holidayHours = 0;
|
|
||||||
let unpaidHours = 0;
|
let unpaidHours = 0;
|
||||||
|
|
||||||
for (const record of records) {
|
for (const record of records) {
|
||||||
@@ -208,26 +209,51 @@ export default function AttendanceHistory() {
|
|||||||
record.leave_hours != null ? Number(record.leave_hours) : 8;
|
record.leave_hours != null ? Number(record.leave_hours) : 8;
|
||||||
if (leaveType === "vacation") vacationHours += hours;
|
if (leaveType === "vacation") vacationHours += hours;
|
||||||
else if (leaveType === "sick") sickHours += hours;
|
else if (leaveType === "sick") sickHours += hours;
|
||||||
else if (leaveType === "holiday") holidayHours += hours;
|
|
||||||
else if (leaveType === "unpaid") unpaidHours += hours;
|
else if (leaveType === "unpaid") unpaidHours += hours;
|
||||||
|
// "holiday" is no longer a user-selectable leave_type — it is
|
||||||
|
// auto-computed from Czech public holidays by the print/KPI code.
|
||||||
|
// Old records may still exist; skip so they don't double-count.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const yr = parseInt(yearStr, 10);
|
const yr = parseInt(yearStr, 10);
|
||||||
const mo = parseInt(monthStr, 10) - 1;
|
const mo = parseInt(monthStr, 10) - 1;
|
||||||
const businessDays = getBusinessDaysInMonth(yr, mo);
|
const businessDays = getBusinessDaysInMonth(yr, mo);
|
||||||
const fund = businessDays * 8;
|
// Fund counts Mon-Fri + holidays (holidays are paid via the fond).
|
||||||
|
const holidayDates = holidaysInMonth(yr, mo + 1);
|
||||||
|
const holidayCount = holidayDates.length;
|
||||||
|
const fund = (businessDays + holidayCount) * 8;
|
||||||
const worked = Math.round((totalMinutes / 60) * 100) / 100;
|
const worked = Math.round((totalMinutes / 60) * 100) / 100;
|
||||||
// Covered = worked + vacation + sick (NOT holiday/unpaid — holiday is excluded from fund, unpaid is voluntary)
|
// Covered = worked + vacation + sick (NOT unpaid — unpaid is voluntary).
|
||||||
const leaveHours = vacationHours + sickHours;
|
const leaveHours = vacationHours + sickHours;
|
||||||
const covered = Math.round((worked + leaveHours) * 100) / 100;
|
const covered = Math.round((worked + leaveHours) * 100) / 100;
|
||||||
const remaining = Math.max(0, Math.round((fund - covered) * 100) / 100);
|
|
||||||
const overtime = Math.max(0, Math.round((covered - fund) * 100) / 100);
|
// fondUsed = real_worked + vacation + worked_holiday + free_holiday.
|
||||||
|
// (the all-in number: everything the user got paid for this month).
|
||||||
|
const realWorked = totalMinutes / 60;
|
||||||
|
const freeHolidayHours = calculateFreeHolidayHours(
|
||||||
|
records as unknown as Parameters<typeof calculateFreeHolidayHours>[0],
|
||||||
|
holidayDates,
|
||||||
|
);
|
||||||
|
const workedHolidayHours = records
|
||||||
|
.filter(
|
||||||
|
(r) =>
|
||||||
|
(r.leave_type || "work") === "work" &&
|
||||||
|
holidayDates.includes(normalizeDateStr(r.shift_date)),
|
||||||
|
)
|
||||||
|
.reduce((sum, r) => sum + calculateWorkMinutesPrint(r) / 60, 0);
|
||||||
|
const fondUsed =
|
||||||
|
realWorked + vacationHours + workedHolidayHours + freeHolidayHours;
|
||||||
|
const delta = fondUsed - fund;
|
||||||
|
const missing = Math.max(0, Math.round(-delta * 100) / 100);
|
||||||
|
const overtime = Math.max(0, Math.round(delta * 100) / 100);
|
||||||
|
const remaining = missing;
|
||||||
|
|
||||||
const monthlyFund = {
|
const monthlyFund = {
|
||||||
fund,
|
fund,
|
||||||
business_days: businessDays,
|
business_days: businessDays,
|
||||||
worked,
|
holiday_count: holidayCount,
|
||||||
|
worked: Math.round(fondUsed * 100) / 100,
|
||||||
covered,
|
covered,
|
||||||
remaining,
|
remaining,
|
||||||
overtime,
|
overtime,
|
||||||
@@ -238,9 +264,12 @@ export default function AttendanceHistory() {
|
|||||||
totalMinutes,
|
totalMinutes,
|
||||||
vacationHours,
|
vacationHours,
|
||||||
sickHours,
|
sickHours,
|
||||||
holidayHours,
|
|
||||||
unpaidHours,
|
unpaidHours,
|
||||||
monthlyFund,
|
monthlyFund,
|
||||||
|
fondUsed,
|
||||||
|
freeHolidayHours,
|
||||||
|
workedHolidayHours,
|
||||||
|
delta,
|
||||||
};
|
};
|
||||||
}, [records, month]);
|
}, [records, month]);
|
||||||
|
|
||||||
@@ -324,7 +353,6 @@ export default function AttendanceHistory() {
|
|||||||
}
|
}
|
||||||
.badge-vacation { background: #dbeafe; color: #1d4ed8; }
|
.badge-vacation { background: #dbeafe; color: #1d4ed8; }
|
||||||
.badge-sick { background: #fee2e2; color: #dc2626; }
|
.badge-sick { background: #fee2e2; color: #dc2626; }
|
||||||
.badge-holiday { background: #dcfce7; color: #16a34a; }
|
|
||||||
.badge-unpaid { background: #f3f4f6; color: #6b7280; }
|
.badge-unpaid { background: #f3f4f6; color: #6b7280; }
|
||||||
.badge-overtime { background: #fef3c7; color: #d97706; }
|
.badge-overtime { background: #fef3c7; color: #d97706; }
|
||||||
@media print {
|
@media print {
|
||||||
@@ -411,11 +439,11 @@ export default function AttendanceHistory() {
|
|||||||
transition={{ duration: 0.25, delay: 0.08 }}
|
transition={{ duration: 0.25, delay: 0.08 }}
|
||||||
>
|
>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<Skeleton
|
{isPending ? (
|
||||||
name="attendance-history-fund"
|
<div className="admin-loading">
|
||||||
loading={isPending}
|
<div className="admin-spinner" />
|
||||||
fixture={<AttendanceHistoryFixture />}
|
</div>
|
||||||
>
|
) : (
|
||||||
<>
|
<>
|
||||||
{computed.monthlyFund && (
|
{computed.monthlyFund && (
|
||||||
<div
|
<div
|
||||||
@@ -465,51 +493,90 @@ export default function AttendanceHistory() {
|
|||||||
style={{ fontSize: "0.8125rem" }}
|
style={{ fontSize: "0.8125rem" }}
|
||||||
>
|
>
|
||||||
{computed.monthlyFund.business_days} prac. dnů
|
{computed.monthlyFund.business_days} prac. dnů
|
||||||
|
{computed.monthlyFund.holiday_count > 0 &&
|
||||||
|
` + ${computed.monthlyFund.holiday_count} svátky`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="attendance-balance-bar">
|
<div className="attendance-balance-bar">
|
||||||
<div
|
<div
|
||||||
className="attendance-balance-progress"
|
className="attendance-balance-progress"
|
||||||
style={{
|
style={{
|
||||||
width: `${Math.min(100, computed.monthlyFund.fund > 0 ? (computed.monthlyFund.covered / computed.monthlyFund.fund) * 100 : 0)}%`,
|
width: `${Math.min(100, computed.monthlyFund.fund > 0 ? (computed.monthlyFund.worked / computed.monthlyFund.fund) * 100 : 0)}%`,
|
||||||
background:
|
background:
|
||||||
computed.monthlyFund.covered >=
|
computed.delta > 0
|
||||||
computed.monthlyFund.fund
|
? "linear-gradient(135deg, var(--warning), #d97706)"
|
||||||
? "linear-gradient(135deg, var(--success), #059669)"
|
: computed.delta >= 0
|
||||||
: "var(--gradient)",
|
? "linear-gradient(135deg, var(--success), #059669)"
|
||||||
|
: "var(--gradient)",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="text-muted"
|
|
||||||
style={{
|
style={{
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
gap: "0.4rem",
|
||||||
|
marginTop: "0.5rem",
|
||||||
|
minHeight: "1.5rem",
|
||||||
|
alignItems: "center",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
fontSize: "0.75rem",
|
|
||||||
marginTop: "0.375rem",
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span>
|
<div
|
||||||
{"Pokryto: "}
|
style={{
|
||||||
{computed.monthlyFund.covered}h (práce{" "}
|
display: "flex",
|
||||||
{computed.monthlyFund.worked}h
|
flexWrap: "wrap",
|
||||||
{computed.vacationHours > 0 &&
|
gap: "0.4rem",
|
||||||
` + dovolená ${computed.vacationHours}h`}
|
}}
|
||||||
{computed.sickHours > 0 &&
|
>
|
||||||
` + nemoc ${computed.sickHours}h`}
|
{computed.totalMinutes > 0 && (
|
||||||
{computed.holidayHours > 0 &&
|
<span
|
||||||
` + svátek ${computed.holidayHours}h`}
|
className="attendance-leave-badge"
|
||||||
{computed.unpaidHours > 0 &&
|
title="Odpracováno (reálně)"
|
||||||
` + neplacené ${computed.unpaidHours}h`}
|
>
|
||||||
)
|
Práce: {formatHoursDecimal(computed.totalMinutes)}h
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{computed.vacationHours > 0 && (
|
||||||
|
<span className="attendance-leave-badge badge-vacation">
|
||||||
|
Dov: {computed.vacationHours}h
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{computed.sickHours > 0 && (
|
||||||
|
<span className="attendance-leave-badge badge-sick">
|
||||||
|
Nem: {computed.sickHours}h
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{computed.freeHolidayHours > 0 && (
|
||||||
|
<span className="attendance-leave-badge badge-holiday">
|
||||||
|
Sv: {Math.round(computed.freeHolidayHours)}h
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{computed.unpaidHours > 0 && (
|
||||||
|
<span className="attendance-leave-badge badge-unpaid">
|
||||||
|
Nep: {computed.unpaidHours}h
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
fontSize: "0.75rem",
|
||||||
|
color: "var(--text-muted)",
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
computed.delta > 0
|
||||||
|
? "text-warning fw-600"
|
||||||
|
: computed.delta < 0
|
||||||
|
? "text-danger fw-600"
|
||||||
|
: "text-success fw-600"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{computed.delta > 0
|
||||||
|
? `Přesčas: +${formatHoursDecimal(computed.delta * 60)}h`
|
||||||
|
: computed.delta < 0
|
||||||
|
? `Zbývá: ${formatHoursDecimal(-computed.delta * 60)}h`
|
||||||
|
: "Fond splněn"}
|
||||||
</span>
|
</span>
|
||||||
{computed.monthlyFund.overtime > 0 ? (
|
|
||||||
<span className="text-warning fw-600">
|
|
||||||
Přesčas: +{computed.monthlyFund.overtime}h
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span>Zbývá: {computed.monthlyFund.remaining}h</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -527,7 +594,7 @@ export default function AttendanceHistory() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
</Skeleton>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
@@ -539,11 +606,11 @@ export default function AttendanceHistory() {
|
|||||||
transition={{ duration: 0.25, delay: 0.12 }}
|
transition={{ duration: 0.25, delay: 0.12 }}
|
||||||
>
|
>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<Skeleton
|
{isPending ? (
|
||||||
name="attendance-history-table"
|
<div className="admin-loading">
|
||||||
loading={isPending}
|
<div className="admin-spinner" />
|
||||||
fixture={<AttendanceHistoryFixture />}
|
</div>
|
||||||
>
|
) : (
|
||||||
<>
|
<>
|
||||||
{records.length === 0 && (
|
{records.length === 0 && (
|
||||||
<div className="admin-empty-state">
|
<div className="admin-empty-state">
|
||||||
@@ -622,7 +689,7 @@ export default function AttendanceHistory() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
</Skeleton>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
@@ -670,9 +737,7 @@ export default function AttendanceHistory() {
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(computed.vacationHours > 0 ||
|
{(computed.vacationHours > 0 || computed.sickHours > 0) && (
|
||||||
computed.sickHours > 0 ||
|
|
||||||
computed.holidayHours > 0) && (
|
|
||||||
<div className="leave-summary">
|
<div className="leave-summary">
|
||||||
{computed.vacationHours > 0 && (
|
{computed.vacationHours > 0 && (
|
||||||
<>
|
<>
|
||||||
@@ -688,13 +753,6 @@ export default function AttendanceHistory() {
|
|||||||
</span>{" "}
|
</span>{" "}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{computed.holidayHours > 0 && (
|
|
||||||
<>
|
|
||||||
<span className="leave-badge badge-holiday">
|
|
||||||
Svátek: {computed.holidayHours}h
|
|
||||||
</span>{" "}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import {
|
|||||||
attendanceLocationOptions,
|
attendanceLocationOptions,
|
||||||
type LocationRecord,
|
type LocationRecord,
|
||||||
} from "../lib/queries/attendance";
|
} from "../lib/queries/attendance";
|
||||||
import { Skeleton } from "boneyard-js/react";
|
|
||||||
import AttendanceLocationFixture from "../fixtures/AttendanceLocationFixture";
|
|
||||||
|
|
||||||
export default function AttendanceLocation() {
|
export default function AttendanceLocation() {
|
||||||
const alert = useAlert();
|
const alert = useAlert();
|
||||||
@@ -168,71 +166,111 @@ export default function AttendanceLocation() {
|
|||||||
: record.shift_date;
|
: record.shift_date;
|
||||||
const month = shiftDateStr.substring(0, 7);
|
const month = shiftDateStr.substring(0, 7);
|
||||||
|
|
||||||
|
if (isPending) {
|
||||||
|
return (
|
||||||
|
<div className="admin-loading">
|
||||||
|
<div className="admin-spinner" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Skeleton
|
<div>
|
||||||
name="attendance-location"
|
<motion.div
|
||||||
loading={isPending}
|
className="admin-page-header"
|
||||||
fixture={<AttendanceLocationFixture />}
|
initial={{ opacity: 0, y: 12 }}
|
||||||
>
|
animate={{ opacity: 1, y: 0 }}
|
||||||
<div>
|
transition={{ duration: 0.25 }}
|
||||||
<motion.div
|
>
|
||||||
className="admin-page-header"
|
<div>
|
||||||
initial={{ opacity: 0, y: 12 }}
|
<h1 className="admin-page-title">Poloha záznamu</h1>
|
||||||
animate={{ opacity: 1, y: 0 }}
|
</div>
|
||||||
transition={{ duration: 0.25 }}
|
<div className="admin-page-actions">
|
||||||
>
|
<Link
|
||||||
<div>
|
to={`/attendance/admin?month=${month}`}
|
||||||
<h1 className="admin-page-title">Poloha záznamu</h1>
|
className="admin-btn admin-btn-secondary"
|
||||||
</div>
|
>
|
||||||
<div className="admin-page-actions">
|
← Zpět na správu
|
||||||
<Link
|
</Link>
|
||||||
to={`/attendance/admin?month=${month}`}
|
</div>
|
||||||
className="admin-btn admin-btn-secondary"
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
className="admin-card"
|
||||||
|
initial={{ opacity: 0, y: 12 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ duration: 0.25, delay: 0.06 }}
|
||||||
|
>
|
||||||
|
<div className="admin-card-header">
|
||||||
|
<h2 className="admin-card-title">
|
||||||
|
{record.user_name} — {formatDate(record.shift_date)}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div className="admin-card-body">
|
||||||
|
{hasAnyLocation && (
|
||||||
|
<div ref={mapRef} className="attendance-location-map" />
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="attendance-location-grid">
|
||||||
|
{/* Arrival */}
|
||||||
|
<div
|
||||||
|
className={`attendance-location-card ${!hasArrivalLocation ? "empty" : ""}`}
|
||||||
>
|
>
|
||||||
← Zpět na správu
|
<h3 className="attendance-location-title">Příchod</h3>
|
||||||
</Link>
|
<div className="attendance-location-time">
|
||||||
</div>
|
{record.arrival_time
|
||||||
</motion.div>
|
? formatDatetimeLocal(record.arrival_time)
|
||||||
|
: "—"}
|
||||||
|
</div>
|
||||||
|
{hasArrivalLocation ? (
|
||||||
|
<>
|
||||||
|
<div className="attendance-location-address">
|
||||||
|
{record.arrival_address || <em>Adresa nezjištěna</em>}
|
||||||
|
</div>
|
||||||
|
<div className="attendance-location-coords">
|
||||||
|
GPS: {record.arrival_lat}, {record.arrival_lng}
|
||||||
|
{record.arrival_accuracy &&
|
||||||
|
` (přesnost: ${Math.round(Number(record.arrival_accuracy))}m)`}
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href={`https://www.google.com/maps?q=${record.arrival_lat},${record.arrival_lng}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="admin-btn admin-btn-secondary admin-btn-sm mt-2"
|
||||||
|
>
|
||||||
|
Otevřít v Google Maps
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="attendance-location-address">
|
||||||
|
<em>Poloha nebyla zaznamenána</em>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<motion.div
|
{/* Departure */}
|
||||||
className="admin-card"
|
{(hasDepartureLocation || record.departure_time) && (
|
||||||
initial={{ opacity: 0, y: 12 }}
|
|
||||||
animate={{ opacity: 1, y: 0 }}
|
|
||||||
transition={{ duration: 0.25, delay: 0.06 }}
|
|
||||||
>
|
|
||||||
<div className="admin-card-header">
|
|
||||||
<h2 className="admin-card-title">
|
|
||||||
{record.user_name} — {formatDate(record.shift_date)}
|
|
||||||
</h2>
|
|
||||||
</div>
|
|
||||||
<div className="admin-card-body">
|
|
||||||
{hasAnyLocation && (
|
|
||||||
<div ref={mapRef} className="attendance-location-map" />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="attendance-location-grid">
|
|
||||||
{/* Arrival */}
|
|
||||||
<div
|
<div
|
||||||
className={`attendance-location-card ${!hasArrivalLocation ? "empty" : ""}`}
|
className={`attendance-location-card ${!hasDepartureLocation ? "empty" : ""}`}
|
||||||
>
|
>
|
||||||
<h3 className="attendance-location-title">Příchod</h3>
|
<h3 className="attendance-location-title">Odchod</h3>
|
||||||
<div className="attendance-location-time">
|
<div className="attendance-location-time">
|
||||||
{record.arrival_time
|
{record.departure_time
|
||||||
? formatDatetimeLocal(record.arrival_time)
|
? formatDatetimeLocal(record.departure_time)
|
||||||
: "—"}
|
: "—"}
|
||||||
</div>
|
</div>
|
||||||
{hasArrivalLocation ? (
|
{hasDepartureLocation ? (
|
||||||
<>
|
<>
|
||||||
<div className="attendance-location-address">
|
<div className="attendance-location-address">
|
||||||
{record.arrival_address || <em>Adresa nezjištěna</em>}
|
{record.departure_address || <em>Adresa nezjištěna</em>}
|
||||||
</div>
|
</div>
|
||||||
<div className="attendance-location-coords">
|
<div className="attendance-location-coords">
|
||||||
GPS: {record.arrival_lat}, {record.arrival_lng}
|
GPS: {record.departure_lat}, {record.departure_lng}
|
||||||
{record.arrival_accuracy &&
|
{record.departure_accuracy &&
|
||||||
` (přesnost: ${Math.round(Number(record.arrival_accuracy))}m)`}
|
` (přesnost: ${Math.round(Number(record.departure_accuracy))}m)`}
|
||||||
</div>
|
</div>
|
||||||
<a
|
<a
|
||||||
href={`https://www.google.com/maps?q=${record.arrival_lat},${record.arrival_lng}`}
|
href={`https://www.google.com/maps?q=${record.departure_lat},${record.departure_lng}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className="admin-btn admin-btn-secondary admin-btn-sm mt-2"
|
className="admin-btn admin-btn-secondary admin-btn-sm mt-2"
|
||||||
@@ -246,48 +284,10 @@ export default function AttendanceLocation() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
{/* Departure */}
|
|
||||||
{(hasDepartureLocation || record.departure_time) && (
|
|
||||||
<div
|
|
||||||
className={`attendance-location-card ${!hasDepartureLocation ? "empty" : ""}`}
|
|
||||||
>
|
|
||||||
<h3 className="attendance-location-title">Odchod</h3>
|
|
||||||
<div className="attendance-location-time">
|
|
||||||
{record.departure_time
|
|
||||||
? formatDatetimeLocal(record.departure_time)
|
|
||||||
: "—"}
|
|
||||||
</div>
|
|
||||||
{hasDepartureLocation ? (
|
|
||||||
<>
|
|
||||||
<div className="attendance-location-address">
|
|
||||||
{record.departure_address || <em>Adresa nezjištěna</em>}
|
|
||||||
</div>
|
|
||||||
<div className="attendance-location-coords">
|
|
||||||
GPS: {record.departure_lat}, {record.departure_lng}
|
|
||||||
{record.departure_accuracy &&
|
|
||||||
` (přesnost: ${Math.round(Number(record.departure_accuracy))}m)`}
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href={`https://www.google.com/maps?q=${record.departure_lat},${record.departure_lng}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="admin-btn admin-btn-secondary admin-btn-sm mt-2"
|
|
||||||
>
|
|
||||||
Otevřít v Google Maps
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div className="attendance-location-address">
|
|
||||||
<em>Poloha nebyla zaznamenána</em>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
</Skeleton>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useAuth } from "../context/AuthContext";
|
import { useAuth } from "../context/AuthContext";
|
||||||
import { useAlert } from "../context/AlertContext";
|
import { useAlert } from "../context/AlertContext";
|
||||||
import Forbidden from "../components/Forbidden";
|
import Forbidden from "../components/Forbidden";
|
||||||
import Pagination from "../components/Pagination";
|
import Pagination from "../components/Pagination";
|
||||||
import FormField from "../components/FormField";
|
import FormField from "../components/FormField";
|
||||||
|
import FormModal from "../components/FormModal";
|
||||||
import AdminDatePicker from "../components/AdminDatePicker";
|
import AdminDatePicker from "../components/AdminDatePicker";
|
||||||
import { czechPlural } from "../utils/formatters";
|
import { czechPlural } from "../utils/formatters";
|
||||||
import apiFetch from "../utils/api";
|
import apiFetch from "../utils/api";
|
||||||
import { Skeleton } from "boneyard-js/react";
|
import { useApiMutation } from "../lib/queries/mutations";
|
||||||
import AuditLogFixture from "../fixtures/AuditLogFixture";
|
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
@@ -91,7 +91,6 @@ interface Filters {
|
|||||||
export default function AuditLog() {
|
export default function AuditLog() {
|
||||||
const { hasPermission } = useAuth();
|
const { hasPermission } = useAuth();
|
||||||
const alert = useAlert();
|
const alert = useAlert();
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const [filters, setFilters] = useState<Filters>({
|
const [filters, setFilters] = useState<Filters>({
|
||||||
search: "",
|
search: "",
|
||||||
action: "",
|
action: "",
|
||||||
@@ -103,7 +102,6 @@ export default function AuditLog() {
|
|||||||
const [perPage, setPerPage] = useState(50);
|
const [perPage, setPerPage] = useState(50);
|
||||||
const [showCleanup, setShowCleanup] = useState(false);
|
const [showCleanup, setShowCleanup] = useState(false);
|
||||||
const [cleanupDays, setCleanupDays] = useState(90);
|
const [cleanupDays, setCleanupDays] = useState(90);
|
||||||
const [cleaning, setCleaning] = useState(false);
|
|
||||||
|
|
||||||
const { data: logsData, isPending } = useQuery({
|
const { data: logsData, isPending } = useQuery({
|
||||||
queryKey: [
|
queryKey: [
|
||||||
@@ -148,13 +146,26 @@ export default function AuditLog() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const logs = logsData?.data ?? [];
|
const logs: AuditLogEntry[] = logsData?.data ?? [];
|
||||||
const pagination = logsData?.pagination ?? null;
|
const pagination = logsData?.pagination ?? null;
|
||||||
|
|
||||||
if (!hasPermission("settings.audit")) {
|
if (!hasPermission("settings.audit")) {
|
||||||
return <Forbidden />;
|
return <Forbidden />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cleanupMutation = useApiMutation<
|
||||||
|
{ days: number; confirm?: string },
|
||||||
|
{ message?: string; error?: string }
|
||||||
|
>({
|
||||||
|
url: () => `${API_BASE}/audit-log/cleanup`,
|
||||||
|
method: () => "POST",
|
||||||
|
invalidate: ["audit-log"],
|
||||||
|
onSuccess: (data) => {
|
||||||
|
alert.success(data?.message || "Hotovo");
|
||||||
|
setShowCleanup(false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const handleFilterChange = (key: keyof Filters, value: string) => {
|
const handleFilterChange = (key: keyof Filters, value: string) => {
|
||||||
setFilters((prev) => ({ ...prev, [key]: value }));
|
setFilters((prev) => ({ ...prev, [key]: value }));
|
||||||
setPage(1);
|
setPage(1);
|
||||||
@@ -170,25 +181,14 @@ export default function AuditLog() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleCleanup = async () => {
|
const handleCleanup = async () => {
|
||||||
setCleaning(true);
|
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(`${API_BASE}/audit-log/cleanup`, {
|
// Backend requires this literal confirmation token when wiping everything.
|
||||||
method: "POST",
|
await cleanupMutation.mutateAsync({
|
||||||
headers: { "Content-Type": "application/json" },
|
days: cleanupDays,
|
||||||
body: JSON.stringify({ days: cleanupDays }),
|
...(cleanupDays === 0 ? { confirm: "DELETE_ALL_AUDIT" } : {}),
|
||||||
});
|
});
|
||||||
const data = await response.json();
|
} catch (e) {
|
||||||
if (data.success) {
|
alert.error(e instanceof Error ? e.message : "Chyba připojení");
|
||||||
alert.success(data.message);
|
|
||||||
setShowCleanup(false);
|
|
||||||
queryClient.invalidateQueries({ queryKey: ["audit-log"] });
|
|
||||||
} else {
|
|
||||||
alert.error(data.error);
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert.error("Chyba připojení");
|
|
||||||
} finally {
|
|
||||||
setCleaning(false);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -199,13 +199,9 @@ export default function AuditLog() {
|
|||||||
|
|
||||||
if (isPending && logs.length === 0) {
|
if (isPending && logs.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Skeleton
|
<div className="admin-loading">
|
||||||
name="audit-log"
|
<div className="admin-spinner" />
|
||||||
loading={isPending && logs.length === 0}
|
</div>
|
||||||
fixture={<AuditLogFixture />}
|
|
||||||
>
|
|
||||||
<div />
|
|
||||||
</Skeleton>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,78 +241,36 @@ export default function AuditLog() {
|
|||||||
</button>
|
</button>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{showCleanup && (
|
<FormModal
|
||||||
<div className="admin-modal-overlay" style={{ opacity: 1 }}>
|
isOpen={showCleanup}
|
||||||
<div
|
onClose={() => !cleanupMutation.isPending && setShowCleanup(false)}
|
||||||
className="admin-modal-backdrop"
|
onSubmit={handleCleanup}
|
||||||
onClick={() => !cleaning && setShowCleanup(false)}
|
title="Vyčistit audit log"
|
||||||
/>
|
submitLabel="Smazat"
|
||||||
<motion.div
|
loading={cleanupMutation.isPending}
|
||||||
className="admin-modal admin-confirm-modal"
|
>
|
||||||
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
<p className="admin-confirm-message">Smazat záznamy starší než:</p>
|
||||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
<div style={{ margin: "0.75rem 0", maxWidth: "200px" }}>
|
||||||
transition={{ duration: 0.2 }}
|
<select
|
||||||
|
className="admin-form-select"
|
||||||
|
value={cleanupDays}
|
||||||
|
onChange={(e) => setCleanupDays(parseInt(e.target.value))}
|
||||||
>
|
>
|
||||||
<div className="admin-modal-body admin-confirm-content">
|
<option value={30}>30 dní</option>
|
||||||
<div className="admin-confirm-icon admin-confirm-icon-danger">
|
<option value={60}>60 dní</option>
|
||||||
<svg
|
<option value={90}>90 dní</option>
|
||||||
width="24"
|
<option value={180}>180 dní</option>
|
||||||
height="24"
|
<option value={365}>1 rok</option>
|
||||||
viewBox="0 0 24 24"
|
<option value={0}>Vše</option>
|
||||||
fill="none"
|
</select>
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="2"
|
|
||||||
>
|
|
||||||
<polyline points="3 6 5 6 21 6" />
|
|
||||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<h2 className="admin-confirm-title">Vyčistit audit log</h2>
|
|
||||||
<p className="admin-confirm-message">
|
|
||||||
Smazat záznamy starší než:
|
|
||||||
</p>
|
|
||||||
<div style={{ margin: "0.75rem auto", maxWidth: "200px" }}>
|
|
||||||
<select
|
|
||||||
className="admin-form-select"
|
|
||||||
value={cleanupDays}
|
|
||||||
onChange={(e) => setCleanupDays(parseInt(e.target.value))}
|
|
||||||
>
|
|
||||||
<option value={30}>30 dní</option>
|
|
||||||
<option value={60}>60 dní</option>
|
|
||||||
<option value={90}>90 dní</option>
|
|
||||||
<option value={180}>180 dní</option>
|
|
||||||
<option value={365}>1 rok</option>
|
|
||||||
<option value={0}>Vše</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<p
|
|
||||||
className="admin-confirm-message"
|
|
||||||
style={{ fontSize: "12px", opacity: 0.6 }}
|
|
||||||
>
|
|
||||||
Tato akce je nevratná.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="admin-modal-footer">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setShowCleanup(false)}
|
|
||||||
className="admin-btn admin-btn-secondary"
|
|
||||||
disabled={cleaning}
|
|
||||||
>
|
|
||||||
Zrušit
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={handleCleanup}
|
|
||||||
className="admin-btn admin-btn-primary"
|
|
||||||
disabled={cleaning}
|
|
||||||
>
|
|
||||||
{cleaning ? "Mažu..." : "Smazat"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<p
|
||||||
|
className="admin-confirm-message"
|
||||||
|
style={{ fontSize: "12px", opacity: 0.6 }}
|
||||||
|
>
|
||||||
|
Tato akce je nevratná.
|
||||||
|
</p>
|
||||||
|
</FormModal>
|
||||||
|
|
||||||
<motion.div
|
<motion.div
|
||||||
className="admin-card mb-4"
|
className="admin-card mb-4"
|
||||||
@@ -391,90 +345,11 @@ export default function AuditLog() {
|
|||||||
>
|
>
|
||||||
<div className="admin-card-body">
|
<div className="admin-card-body">
|
||||||
<div className="admin-table-responsive">
|
<div className="admin-table-responsive">
|
||||||
<Skeleton
|
{isPending ? (
|
||||||
name="audit-log-rows"
|
<div className="admin-loading">
|
||||||
loading={isPending}
|
<div className="admin-spinner" />
|
||||||
fixture={
|
</div>
|
||||||
<table className="admin-table">
|
) : (
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Čas</th>
|
|
||||||
<th>Uživatel</th>
|
|
||||||
<th>Akce</th>
|
|
||||||
<th>Typ entity</th>
|
|
||||||
<th>Popis</th>
|
|
||||||
<th>IP</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{Array.from({ length: 10 }, (_, i) => (
|
|
||||||
<tr key={i}>
|
|
||||||
<td>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 110,
|
|
||||||
height: 14,
|
|
||||||
background: "var(--bg-tertiary)",
|
|
||||||
borderRadius: 4,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 80,
|
|
||||||
height: 14,
|
|
||||||
background: "var(--bg-tertiary)",
|
|
||||||
borderRadius: 4,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 70,
|
|
||||||
height: 22,
|
|
||||||
background: "var(--bg-tertiary)",
|
|
||||||
borderRadius: 10,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 80,
|
|
||||||
height: 14,
|
|
||||||
background: "var(--bg-tertiary)",
|
|
||||||
borderRadius: 4,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
height: 14,
|
|
||||||
background: "var(--bg-tertiary)",
|
|
||||||
borderRadius: 4,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
width: 90,
|
|
||||||
height: 14,
|
|
||||||
background: "var(--bg-tertiary)",
|
|
||||||
borderRadius: 4,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<table className="admin-table">
|
<table className="admin-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -536,7 +411,7 @@ export default function AuditLog() {
|
|||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</Skeleton>
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Pagination
|
<Pagination
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -9,14 +9,13 @@ import apiFetch from "../utils/api";
|
|||||||
import { dashboardOptions } from "../lib/queries/dashboard";
|
import { dashboardOptions } from "../lib/queries/dashboard";
|
||||||
import { require2FAOptions } from "../lib/queries/settings";
|
import { require2FAOptions } from "../lib/queries/settings";
|
||||||
import { getCzechDate } from "../utils/dashboardHelpers";
|
import { getCzechDate } from "../utils/dashboardHelpers";
|
||||||
|
import { useApiMutation } from "../lib/queries/mutations";
|
||||||
import DashKpiCards from "../components/dashboard/DashKpiCards";
|
import DashKpiCards from "../components/dashboard/DashKpiCards";
|
||||||
import DashQuickActions from "../components/dashboard/DashQuickActions";
|
import DashQuickActions from "../components/dashboard/DashQuickActions";
|
||||||
import DashActivityFeed from "../components/dashboard/DashActivityFeed";
|
import DashActivityFeed from "../components/dashboard/DashActivityFeed";
|
||||||
import DashAttendanceToday from "../components/dashboard/DashAttendanceToday";
|
import DashAttendanceToday from "../components/dashboard/DashAttendanceToday";
|
||||||
import DashProfile from "../components/dashboard/DashProfile";
|
import DashProfile from "../components/dashboard/DashProfile";
|
||||||
import DashSessions from "../components/dashboard/DashSessions";
|
import DashSessions from "../components/dashboard/DashSessions";
|
||||||
import { Skeleton } from "boneyard-js/react";
|
|
||||||
import DashboardFixture from "../fixtures/DashboardFixture";
|
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
@@ -84,6 +83,15 @@ export default function Dashboard() {
|
|||||||
useQuery(require2FAOptions());
|
useQuery(require2FAOptions());
|
||||||
const totpEnabled = totpData?.require_2fa ?? !!user?.totpEnabled;
|
const totpEnabled = totpData?.require_2fa ?? !!user?.totpEnabled;
|
||||||
|
|
||||||
|
const punchMutation = useApiMutation<
|
||||||
|
Record<string, unknown>,
|
||||||
|
{ message?: string }
|
||||||
|
>({
|
||||||
|
url: () => `${API_BASE}/attendance`,
|
||||||
|
method: () => "POST",
|
||||||
|
invalidate: ["attendance", "dashboard"],
|
||||||
|
});
|
||||||
|
|
||||||
// 2FA state - sdileny mezi profilem a bannerem
|
// 2FA state - sdileny mezi profilem a bannerem
|
||||||
const [show2FASetup, setShow2FASetup] = useState(false);
|
const [show2FASetup, setShow2FASetup] = useState(false);
|
||||||
const [show2FADisable, setShow2FADisable] = useState(false);
|
const [show2FADisable, setShow2FADisable] = useState(false);
|
||||||
@@ -104,21 +112,13 @@ export default function Dashboard() {
|
|||||||
|
|
||||||
const submitPunch = async (gpsData: Record<string, unknown> = {}) => {
|
const submitPunch = async (gpsData: Record<string, unknown> = {}) => {
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(`${API_BASE}/attendance`, {
|
const result = await punchMutation.mutateAsync({
|
||||||
method: "POST",
|
punch_action: action,
|
||||||
headers: { "Content-Type": "application/json" },
|
...gpsData,
|
||||||
body: JSON.stringify({ punch_action: action, ...gpsData }),
|
|
||||||
});
|
});
|
||||||
const result = await response.json();
|
alert.success(result?.message || "Docházka zaznamenána");
|
||||||
if (result.success) {
|
} catch (e) {
|
||||||
alert.success(result.data?.message || "Docházka zaznamenána");
|
alert.error(e instanceof Error ? e.message : "Chyba pripojeni");
|
||||||
queryClient.invalidateQueries({ queryKey: ["dashboard"] });
|
|
||||||
queryClient.invalidateQueries({ queryKey: ["attendance"] });
|
|
||||||
} else {
|
|
||||||
alert.error(result.error || "Chyba při záznamu docházky");
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
alert.error("Chyba pripojeni");
|
|
||||||
} finally {
|
} finally {
|
||||||
setPunching(false);
|
setPunching(false);
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@ export default function Dashboard() {
|
|||||||
() => submitPunch({}),
|
() => submitPunch({}),
|
||||||
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 },
|
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 },
|
||||||
);
|
);
|
||||||
}, [dashData, alert, queryClient]);
|
}, [dashData, alert, punchMutation]);
|
||||||
|
|
||||||
// 2FA handlery
|
// 2FA handlery
|
||||||
const handleStart2FASetup = async () => {
|
const handleStart2FASetup = async () => {
|
||||||
@@ -309,15 +309,11 @@ export default function Dashboard() {
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Skeleton loading */}
|
{/* Loading spinner */}
|
||||||
{dashLoading && (
|
{dashLoading && (
|
||||||
<Skeleton
|
<div className="admin-loading">
|
||||||
name="dashboard"
|
<div className="admin-spinner" />
|
||||||
loading={dashLoading}
|
</div>
|
||||||
fixture={<DashboardFixture />}
|
|
||||||
>
|
|
||||||
<div />
|
|
||||||
</Skeleton>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* KPI cards — only show if user has any admin-level permissions */}
|
{/* KPI cards — only show if user has any admin-level permissions */}
|
||||||
@@ -325,12 +321,14 @@ export default function Dashboard() {
|
|||||||
(hasPermission("offers.view") ||
|
(hasPermission("offers.view") ||
|
||||||
hasPermission("invoices.view") ||
|
hasPermission("invoices.view") ||
|
||||||
hasPermission("projects.view") ||
|
hasPermission("projects.view") ||
|
||||||
hasPermission("orders.view")) && <DashKpiCards dashData={dashData} />}
|
hasPermission("orders.view")) && (
|
||||||
|
<DashKpiCards dashData={dashData ?? null} />
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Quick actions */}
|
{/* Quick actions */}
|
||||||
{!dashLoading && (
|
{!dashLoading && (
|
||||||
<DashQuickActions
|
<DashQuickActions
|
||||||
dashData={dashData}
|
dashData={dashData ?? null}
|
||||||
punching={punching}
|
punching={punching}
|
||||||
onPunch={handleQuickPunch}
|
onPunch={handleQuickPunch}
|
||||||
/>
|
/>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user