Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59b478f262 | ||
|
|
e4f14a24b7 | ||
|
|
3bd0d055d9 | ||
|
|
746d17e182 | ||
|
|
e96e51598a | ||
|
|
9abec36f07 | ||
|
|
ecd8e3679f | ||
|
|
ba95723b61 | ||
|
|
12289bdce3 | ||
|
|
d1c5234a03 | ||
|
|
27cc876e82 | ||
|
|
82919d39f6 | ||
|
|
3481b97d47 | ||
|
|
d7c7fbad88 | ||
|
|
7f07032bf2 | ||
|
|
d873c96ae3 | ||
|
|
c4f6723042 | ||
|
|
9e699c4dd4 | ||
|
|
ea81380225 | ||
|
|
a9bc82fac5 | ||
|
|
8c278be941 | ||
|
|
aa6c1b5094 | ||
|
|
4f4b12f039 | ||
|
|
528e55991b | ||
|
|
122eee175e | ||
|
|
5a28f75303 | ||
|
|
07cb428287 | ||
|
|
b197017644 | ||
|
|
e9f07a4a39 | ||
|
|
44d389201c | ||
|
|
3106aaf314 | ||
|
|
90e797b8fa | ||
|
|
1f7362c8af | ||
|
|
fe44a2b12d | ||
|
|
8a9239311d | ||
|
|
cd25cd6ee4 | ||
|
|
967fbba2a4 |
@@ -7,13 +7,13 @@ HOST=127.0.0.1
|
||||
APP_ENV=local
|
||||
|
||||
# Auth — MUST regenerate for production: openssl rand -hex 32
|
||||
JWT_SECRET=generate-with-openssl-rand-hex-32
|
||||
JWT_SECRET=REPLACE_WITH_64_CHAR_HEX_STRING_RUN_openssl_rand_hex_32
|
||||
ACCESS_TOKEN_EXPIRY=900
|
||||
REFRESH_TOKEN_SESSION_EXPIRY=3600
|
||||
REFRESH_TOKEN_REMEMBER_EXPIRY=2592000
|
||||
|
||||
# TOTP — MUST regenerate for production: openssl rand -hex 32
|
||||
TOTP_ENCRYPTION_KEY=generate-with-openssl-rand-hex-32
|
||||
TOTP_ENCRYPTION_KEY=REPLACE_WITH_64_CHAR_HEX_STRING_RUN_openssl_rand_hex_32
|
||||
|
||||
# File storage
|
||||
NAS_PATH=Z:/02_PROJEKTY
|
||||
|
||||
303
CLAUDE.md
Normal file
303
CLAUDE.md
Normal file
@@ -0,0 +1,303 @@
|
||||
# CLAUDE.md — boha-app-ts
|
||||
|
||||
Business management system for a Czech company, rewritten from PHP to TypeScript/Node.js.
|
||||
Handles attendance, invoicing, leave/trips, projects, vehicles, and HR operations.
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack
|
||||
|
||||
| Layer | Technology |
|
||||
| -------------- | ------------------------------------------------------------- |
|
||||
| Runtime | Node.js, TypeScript 5.9.3 (strict) |
|
||||
| HTTP Framework | Fastify 5.8.2 |
|
||||
| ORM | Prisma 6.19.2 → MySQL |
|
||||
| Auth | JWT (HS256, 15 min) + TOTP 2FA (RFC 6238, otpauth) + bcryptjs |
|
||||
| Validation | Zod 4.3.6 |
|
||||
| Frontend | React 18.3.1 + Vite 8.0.0 |
|
||||
| Testing | Vitest 4.1.0 + Supertest |
|
||||
| PDF | Puppeteer 24.x |
|
||||
| Email | nodemailer 8.x |
|
||||
| Cron | node-cron 4.x |
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── server.ts # Fastify server entry point — plugins, routes, error handler
|
||||
├── routes/admin/ # HTTP route handlers (one file per entity)
|
||||
├── services/ # Business logic (no classes, exported functions, uses Prisma directly)
|
||||
├── schemas/ # Zod validation schemas (one file per entity)
|
||||
├── middleware/ # auth.ts (requireAuth, requirePermission, optionalAuth)
|
||||
│ # security.ts (CSP, HSTS, security headers)
|
||||
├── utils/ # totp.ts, pdf.ts, email.ts, audit.ts, formatters, etc.
|
||||
├── config/ # env.ts (config singleton, Date.toJSON override)
|
||||
├── types/ # index.ts (AuthData, JwtPayload, ApiResponse, re-exports from Prisma)
|
||||
├── admin/ # React 18 frontend (57 .tsx files)
|
||||
│ ├── AdminApp.tsx # Router + lazy-loaded pages
|
||||
│ ├── contexts/ # AuthContext, AlertContext
|
||||
│ ├── components/ # Layout, modals, tables, editors
|
||||
│ ├── pages/ # One file per page/feature
|
||||
│ ├── hooks/ # useApiCall, useListData, useTableSort, etc.
|
||||
│ └── utils/ # api.ts (fetch wrapper with token refresh), formatters, helpers
|
||||
└── __tests__/ # Vitest tests (auth, numbering)
|
||||
|
||||
prisma/
|
||||
├── schema.prisma # 32 models, MySQL, snake_case columns
|
||||
└── migrations/ # Applied migrations
|
||||
|
||||
dist/ # Compiled server (CommonJS, ES2022)
|
||||
dist-client/ # Built frontend (Vite, ES2020)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Development
|
||||
npm run dev # Starts server in watch mode (manage frontend separately)
|
||||
npm run dev:server # tsx watch src/server.ts
|
||||
npm run dev:client # Vite dev server
|
||||
|
||||
# Build
|
||||
npm run build # Build server + client
|
||||
npm run build:server # tsc -p tsconfig.server.json → dist/
|
||||
npm run build:client # vite build → dist-client/
|
||||
|
||||
# Run (production)
|
||||
npm start # node dist/server.js
|
||||
|
||||
# Tests
|
||||
npm test # vitest run (single pass)
|
||||
npm run test:watch # vitest watch
|
||||
|
||||
# Database
|
||||
npx prisma migrate dev # Apply migrations (dev)
|
||||
npx prisma migrate deploy # Apply migrations (prod)
|
||||
npx prisma generate # Regenerate Prisma client after schema changes
|
||||
npx prisma studio # DB browser GUI
|
||||
```
|
||||
|
||||
**Do not start the dev server.** The user manages it separately.
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Required:
|
||||
|
||||
```
|
||||
DATABASE_URL=mysql://user:pass@host:3306/dbname
|
||||
JWT_SECRET=<64-char hex string>
|
||||
TOTP_ENCRYPTION_KEY=<64-char hex string>
|
||||
```
|
||||
|
||||
Optional (with defaults):
|
||||
|
||||
```
|
||||
PORT=3001 # Production port (dev default: 3000)
|
||||
HOST=127.0.0.1
|
||||
APP_ENV=local|production # Default: local. Controls CSP, CORS, HSTS
|
||||
ACCESS_TOKEN_EXPIRY=900 # 15 minutes
|
||||
REFRESH_TOKEN_SESSION_EXPIRY=3600 # 1 hour
|
||||
REFRESH_TOKEN_REMEMBER_EXPIRY=2592000 # 30 days
|
||||
NAS_PATH=Z:/02_PROJEKTY # Network share for project files
|
||||
MAX_UPLOAD_SIZE=52428800 # 50MB
|
||||
CONTACT_EMAIL_TO=
|
||||
CONTACT_EMAIL_FROM=
|
||||
SMTP_FROM=
|
||||
LEAVE_NOTIFY_EMAIL=
|
||||
APP_URL= # Used in email links
|
||||
CORS_ORIGINS= # Comma-separated, production only
|
||||
```
|
||||
|
||||
Use `.env` for dev, `.env.test` for tests.
|
||||
|
||||
---
|
||||
|
||||
## Architecture & Key Patterns
|
||||
|
||||
### Request Flow
|
||||
|
||||
```
|
||||
Request → CORS → Cookie → Rate-limit → Security headers
|
||||
→ requirePermission() or requireAuth()
|
||||
→ Zod schema validation (parseBody helper)
|
||||
→ Route handler
|
||||
→ Service function
|
||||
→ Prisma
|
||||
→ success(reply, data) or error(reply, message, status)
|
||||
```
|
||||
|
||||
### Response Format
|
||||
|
||||
All responses use this shape:
|
||||
|
||||
```typescript
|
||||
// Success
|
||||
{ success: true, data: T, message?: string, pagination?: {...} }
|
||||
|
||||
// Error
|
||||
{ success: false, error: string }
|
||||
```
|
||||
|
||||
Use the `success()` and `error()` helpers in routes — never write raw `reply.send()`.
|
||||
|
||||
### Service Pattern
|
||||
|
||||
Services are plain exported async functions, no classes:
|
||||
|
||||
```typescript
|
||||
// src/services/foo.service.ts
|
||||
export async function getFoo(id: number) {
|
||||
const result = await prisma.foo.findUnique({ where: { id } });
|
||||
if (!result) return { error: "Not found", status: 404 };
|
||||
return { data: result };
|
||||
}
|
||||
|
||||
// src/routes/admin/foo.ts
|
||||
const result = await getFoo(id);
|
||||
if ("error" in result) return error(reply, result.error, result.status ?? 400);
|
||||
return success(reply, result.data);
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
- Routes map service errors to HTTP responses using the pattern above.
|
||||
- Global error handler in `server.ts` catches all unhandled exceptions; returns 500 with Czech message.
|
||||
- **Never silently swallow errors.** Even if a failure is non-fatal, log it: `app.log.error(e, 'context')`.
|
||||
- Error messages are in Czech (this is intentional — user-facing messages, Czech company).
|
||||
|
||||
### Permissions
|
||||
|
||||
```typescript
|
||||
// Route-level guard
|
||||
fastify.addHook("preHandler", requirePermission("invoices.view"));
|
||||
// or multiple
|
||||
fastify.addHook(
|
||||
"preHandler",
|
||||
requirePermission("invoices.view", "invoices.edit"),
|
||||
);
|
||||
|
||||
// Admin role bypasses all permission checks
|
||||
// Permissions follow the pattern: "entity.action" (e.g., "users.create", "invoices.delete")
|
||||
```
|
||||
|
||||
### Audit Logging
|
||||
|
||||
Call `logAudit()` from `src/utils/audit.ts` whenever data is created/updated/deleted.
|
||||
Pass `oldData` and `newData` so the diff is stored. Audit failures are non-fatal.
|
||||
|
||||
### Validation
|
||||
|
||||
Use Zod schemas from `src/schemas/`. All route bodies must be validated:
|
||||
|
||||
```typescript
|
||||
const body = parseBody(FooSchema, request.body);
|
||||
if ("error" in body) return error(reply, body.error, 400);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Date & Timezone Handling (Critical Gotcha)
|
||||
|
||||
`src/config/env.ts` sets `process.env.TZ = 'Europe/Prague'` and overrides
|
||||
`Date.prototype.toJSON()` to return local time (not UTC). This means:
|
||||
|
||||
- `JSON.stringify(new Date())` returns local Czech time, not UTC.
|
||||
- All API responses with Date fields will contain local time strings.
|
||||
- Prisma stores dates as UTC internally, but they read back as local due to the TZ setting.
|
||||
- **Never assume UTC** when working with Date objects in this codebase.
|
||||
- When writing new date comparisons or DB queries, use `new Date()` (already local) — do not manually offset.
|
||||
- The override exists for PHP migration compatibility and Czech date display.
|
||||
|
||||
---
|
||||
|
||||
## TOTP / 2FA
|
||||
|
||||
- Secret stored AES-256-GCM encrypted in `users.totp_secret`.
|
||||
- Supports two encoding formats: PHP legacy (base64 iv+cipher+tag) and TS (hex).
|
||||
- Backup codes stored as encrypted JSON array in `users.totp_backup_codes`.
|
||||
- When `company_settings.require_2fa = true`, all users must enroll before accessing the app.
|
||||
- Login flow: password → if 2FA enabled → issue `loginToken` (5 min, single-use) → TOTP verify → issue access + refresh tokens.
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
Tests live in `src/__tests__/`. They use Vitest + Supertest against a real test database (`.env.test`).
|
||||
|
||||
- Test coverage is minimal: only `auth` and `numbering` are tested.
|
||||
- Use `buildApp()` helper to spin up the Fastify instance for tests.
|
||||
- Tests use `vitest.config.ts` with `environment: 'node'` and 15s timeout.
|
||||
- **Do not mock Prisma** — tests hit a real database to catch schema/query bugs.
|
||||
|
||||
When adding new features, add tests in `src/__tests__/`. Name test files `<feature>.test.ts`.
|
||||
|
||||
---
|
||||
|
||||
## Frontend Conventions
|
||||
|
||||
- Pages are lazy-loaded via `React.lazy()` in `AdminApp.tsx`.
|
||||
- Auth state lives in `AuthContext`; use `useAuth()` hook to access it.
|
||||
- Alerts/toasts use `AlertContext`; use `useAlert()` to show them.
|
||||
- API calls go through `src/admin/utils/api.ts` which handles token refresh automatically (deduplicates concurrent refresh calls).
|
||||
- Custom hooks: `useApiCall`, `useListData`, `useTableSort`, `useDebounce`, `useModalLock`.
|
||||
- Styling: CSS files in `src/admin/` — no CSS-in-JS, no Tailwind. Use CSS variables.
|
||||
|
||||
---
|
||||
|
||||
## Database Conventions
|
||||
|
||||
- All models use `snake_case` column names; Prisma maps to camelCase in TypeScript.
|
||||
- Soft-delete via `is_deleted` boolean (not all tables, check schema).
|
||||
- Timestamps: `created_at`, `updated_at` (auto-managed by Prisma).
|
||||
- Number sequences (`number_sequences` table) manage invoice/quotation numbering — never hardcode numbering logic.
|
||||
- All significant tables have audit log entries. Check `audit_logs` model for the schema.
|
||||
|
||||
---
|
||||
|
||||
## Known Issues & Gotchas
|
||||
|
||||
1. **Date.prototype.toJSON override** — global monkey-patch in `src/config/env.ts`. Side-effects on third-party libraries that serialize dates. Do not remove without migrating all date serialization.
|
||||
|
||||
2. **CJS/ESM mismatch in tests** — Server compiles to CommonJS (`tsconfig.server.json`), but Vitest runs in ESM by default. The `vitest.config.ts` resolves this, but be careful when adding dependencies that only support ESM.
|
||||
|
||||
3. **Mixed error patterns** — Some services return `{ error, status }`, others return discriminated unions `{ type: 'success' | 'error' }`. Prefer `{ error, status }` for consistency with existing routes.
|
||||
|
||||
4. **Silent error catches** — A few service functions swallow errors in catch blocks. Always log at minimum; never use empty catch blocks.
|
||||
|
||||
5. **HTML sanitization gap** — Rich text fields in invoices use DOMPurify, but quotation scope and order scope fields may not. If modifying those, add sanitization.
|
||||
|
||||
6. **Puppeteer PDF generation** — Runs a headless browser. Input to the HTML template must be sanitized. Do not pass unsanitized user data into PDF templates.
|
||||
|
||||
7. **NAS_PATH file access** — Project file uploads write to a network share path. In dev, this path may not be mounted. Features using `NAS_PATH` will fail gracefully (or not) if the path is unavailable.
|
||||
|
||||
8. **Prisma client regeneration** — After any schema change, run `npx prisma generate`. The generated client is not committed to git.
|
||||
|
||||
9. **No CSRF tokens** — CSRF protection relies on `SameSite=Strict` cookies + CORS. Do not weaken CORS configuration.
|
||||
|
||||
10. **Czech locale hardcoded** — Error messages, month names, and some business logic strings are Czech. This is intentional.
|
||||
|
||||
---
|
||||
|
||||
## Release Process
|
||||
|
||||
1. Bump version in `package.json`
|
||||
2. `npm run build`
|
||||
3. Commit and tag (`git tag -a vX.Y.Z`)
|
||||
4. Push to Gitea (`git push origin master && git push origin vX.Y.Z`)
|
||||
5. Create tarball: `tar -czf app-ts-X.Y.Z.tar.gz dist dist-client prisma package.json package-lock.json scripts`
|
||||
6. Deploy via SSH to production server (`boha_admin@192.168.50.100`):
|
||||
- Path: `/var/www/app-ts`
|
||||
- Remove old files: `rm -rf dist dist-client prisma scripts package.json package-lock.json`
|
||||
- Copy tarball to server: `scp app-ts-X.Y.Z.tar.gz boha_admin@192.168.50.100:/tmp/`
|
||||
- Extract tarball: `tar -xzf /tmp/app-ts-X.Y.Z.tar.gz`
|
||||
- Install dependencies: `npm install --omit=dev`
|
||||
- Apply Prisma migrations: `npx prisma migrate deploy`
|
||||
- Restart: `pm2 restart app-ts --update-env`
|
||||
|
||||
Do not push directly to production or restart services without confirmation.
|
||||
37
boneyard.config.json
Normal file
37
boneyard.config.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"breakpoints": [375, 768, 1280],
|
||||
"out": "./src/admin/bones",
|
||||
"color": "#e0e0e0",
|
||||
"animate": "shimmer",
|
||||
"shimmerColor": "#f0f0f0",
|
||||
"speed": "1.2s",
|
||||
"shimmerAngle": 110,
|
||||
"wait": 3000,
|
||||
"routes": [
|
||||
"/",
|
||||
"/users",
|
||||
"/attendance",
|
||||
"/attendance/history",
|
||||
"/attendance/admin",
|
||||
"/attendance/balances",
|
||||
"/attendance/requests",
|
||||
"/attendance/approval",
|
||||
"/attendance/create",
|
||||
"/trips",
|
||||
"/trips/history",
|
||||
"/trips/admin",
|
||||
"/vehicles",
|
||||
"/offers",
|
||||
"/offers/new",
|
||||
"/offers/customers",
|
||||
"/offers/templates",
|
||||
"/orders",
|
||||
"/orders/1",
|
||||
"/projects",
|
||||
"/projects/80",
|
||||
"/invoices",
|
||||
"/invoices/new",
|
||||
"/settings",
|
||||
"/audit-log"
|
||||
]
|
||||
}
|
||||
749
package-lock.json
generated
749
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app-ts",
|
||||
"version": "1.4.6",
|
||||
"version": "1.6.2",
|
||||
"description": "",
|
||||
"main": "dist/server.js",
|
||||
"scripts": {
|
||||
@@ -17,7 +17,8 @@
|
||||
"db:push": "prisma db push",
|
||||
"db:studio": "prisma studio",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
"test:watch": "vitest",
|
||||
"bones": "boneyard-js build http://localhost:3000"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
@@ -34,7 +35,10 @@
|
||||
"@fastify/rate-limit": "^10.3.0",
|
||||
"@fastify/static": "^9.0.0",
|
||||
"@prisma/client": "^6.19.2",
|
||||
"@tanstack/react-query": "^5.100.5",
|
||||
"@types/jsdom": "^28.0.1",
|
||||
"bcryptjs": "^3.0.3",
|
||||
"boneyard-js": "^1.8.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"dompurify": "^3.3.3",
|
||||
"dotenv": "^17.3.1",
|
||||
@@ -42,6 +46,7 @@
|
||||
"file-type": "^16.5.4",
|
||||
"framer-motion": "^12.38.0",
|
||||
"hi-base32": "^0.5.1",
|
||||
"jsdom": "^29.0.2",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"leaflet": "^1.9.4",
|
||||
"node-cron": "^4.2.1",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add unique constraint on number_sequences(type, year) for atomic numbering
|
||||
ALTER TABLE number_sequences ADD UNIQUE INDEX idx_number_sequences_type_year (`type`, `year`);
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE `users` ADD COLUMN `totp_last_used_counter` INTEGER NULL;
|
||||
@@ -32,7 +32,7 @@ model attendance {
|
||||
users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "attendance_ibfk_1")
|
||||
attendance_project_logs attendance_project_logs[]
|
||||
|
||||
@@index([user_id, shift_date], map: "idx_attendance_user_date")
|
||||
@@unique([user_id, shift_date], map: "idx_attendance_user_date")
|
||||
@@index([user_id, departure_time], map: "idx_attendance_user_departure")
|
||||
@@index([project_id], map: "idx_project_id")
|
||||
}
|
||||
@@ -46,6 +46,7 @@ model attendance_project_logs {
|
||||
hours Int? @db.UnsignedInt
|
||||
minutes Int? @db.UnsignedInt
|
||||
attendance attendance @relation(fields: [attendance_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
||||
projects projects? @relation(fields: [project_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
|
||||
|
||||
@@index([attendance_id], map: "idx_attendance_project_logs_aid")
|
||||
@@index([project_id], map: "idx_project_id")
|
||||
@@ -104,7 +105,7 @@ model company_settings {
|
||||
quotation_prefix String? @db.VarChar(20)
|
||||
default_currency String? @default("CZK") @db.VarChar(10)
|
||||
default_vat_rate Decimal? @default(21.00) @db.Decimal(5, 2)
|
||||
uuid String? @db.VarChar(36)
|
||||
uuid String? @unique @db.VarChar(36)
|
||||
modified_at DateTime? @db.DateTime(0)
|
||||
is_deleted Boolean? @default(false)
|
||||
sync_version Int? @default(0)
|
||||
@@ -165,7 +166,7 @@ model invoice_items {
|
||||
|
||||
model invoices {
|
||||
id Int @id @default(autoincrement())
|
||||
invoice_number String? @db.VarChar(50)
|
||||
invoice_number String? @unique(map: "idx_invoices_number_unique") @db.VarChar(50)
|
||||
order_id Int?
|
||||
customer_id Int?
|
||||
status String? @default("issued") @db.VarChar(30)
|
||||
@@ -196,6 +197,7 @@ model invoices {
|
||||
@@index([customer_id], map: "customer_id")
|
||||
@@index([due_date], map: "idx_invoices_due_date")
|
||||
@@index([status, issue_date], map: "idx_invoices_status_issue")
|
||||
@@index([status, due_date], map: "idx_invoices_status_due")
|
||||
@@index([order_id], map: "order_id")
|
||||
}
|
||||
|
||||
@@ -253,6 +255,8 @@ model number_sequences {
|
||||
type String? @db.VarChar(50)
|
||||
year Int?
|
||||
last_number Int? @default(0)
|
||||
|
||||
@@unique([type, year], map: "idx_number_sequences_type_year")
|
||||
}
|
||||
|
||||
model order_items {
|
||||
@@ -286,7 +290,7 @@ model order_sections {
|
||||
|
||||
model orders {
|
||||
id Int @id @default(autoincrement())
|
||||
order_number String? @db.VarChar(50)
|
||||
order_number String? @unique(map: "idx_orders_number_unique") @db.VarChar(50)
|
||||
customer_order_number String? @db.VarChar(100)
|
||||
attachment_data Bytes?
|
||||
attachment_name String? @db.VarChar(255)
|
||||
@@ -338,7 +342,7 @@ model project_notes {
|
||||
|
||||
model projects {
|
||||
id Int @id @default(autoincrement())
|
||||
project_number String? @db.VarChar(50)
|
||||
project_number String? @unique @db.VarChar(50)
|
||||
name String? @db.VarChar(255)
|
||||
customer_id Int?
|
||||
responsible_user_id Int?
|
||||
@@ -350,6 +354,7 @@ model projects {
|
||||
notes String? @db.Text
|
||||
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||
modified_at DateTime? @db.DateTime(0)
|
||||
attendance_project_logs attendance_project_logs[]
|
||||
project_notes project_notes[]
|
||||
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")
|
||||
@@ -383,7 +388,7 @@ model quotation_items {
|
||||
|
||||
model quotations {
|
||||
id Int @id @default(autoincrement())
|
||||
quotation_number String? @db.VarChar(50)
|
||||
quotation_number String? @unique @db.VarChar(50)
|
||||
project_code String? @db.VarChar(50)
|
||||
customer_id Int?
|
||||
created_at DateTime? @default(now()) @db.DateTime(0)
|
||||
@@ -432,7 +437,7 @@ model received_invoices {
|
||||
file_mime String? @db.VarChar(100)
|
||||
file_size Int? @db.UnsignedInt
|
||||
notes String? @db.Text
|
||||
uploaded_by Int? @db.UnsignedInt
|
||||
uploaded_by Int?
|
||||
created_at DateTime @default(now()) @db.DateTime(0)
|
||||
modified_at DateTime @default(now()) @db.DateTime(0)
|
||||
|
||||
@@ -444,7 +449,7 @@ model received_invoices {
|
||||
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
|
||||
model refresh_tokens {
|
||||
id Int @id @default(autoincrement()) @db.UnsignedInt
|
||||
user_id Int @db.UnsignedInt
|
||||
user_id Int
|
||||
token_hash String @unique(map: "token_hash") @db.VarChar(64)
|
||||
expires_at DateTime @db.DateTime(0)
|
||||
replaced_at DateTime? @db.DateTime(0)
|
||||
@@ -578,6 +583,7 @@ model users {
|
||||
totp_secret String? @db.VarChar(255)
|
||||
totp_enabled Boolean @default(false)
|
||||
totp_backup_codes String? @db.Text
|
||||
totp_last_used_counter Int?
|
||||
attendance attendance[]
|
||||
leave_balances leave_balances[]
|
||||
leave_requests_leave_requests_user_idTousers leave_requests[] @relation("leave_requests_user_idTousers")
|
||||
@@ -624,6 +630,7 @@ model invoice_alert_log {
|
||||
invoice_id Int
|
||||
alert_type String @db.VarChar(20) // "3days" or "due"
|
||||
sent_at DateTime @default(now()) @db.DateTime(0)
|
||||
created_at DateTime @default(now()) @db.DateTime(0)
|
||||
|
||||
@@unique([invoice_type, invoice_id, alert_type])
|
||||
}
|
||||
|
||||
49
src/__tests__/auth.service.test.ts
Normal file
49
src/__tests__/auth.service.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { verifyAccessToken, hashToken } from "../services/auth";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { config } from "../config/env";
|
||||
|
||||
describe("auth service", () => {
|
||||
describe("verifyAccessToken", () => {
|
||||
it("returns null and logs error for invalid JWT", async () => {
|
||||
const consoleSpy = vi
|
||||
.spyOn(console, "error")
|
||||
.mockImplementation(() => {});
|
||||
const result = await verifyAccessToken("invalid-token");
|
||||
expect(result).toBeNull();
|
||||
expect(consoleSpy).toHaveBeenCalled();
|
||||
expect(consoleSpy.mock.calls[0][0]).toMatch(/JWT verification error/);
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("returns null for expired JWT", async () => {
|
||||
const consoleSpy = vi
|
||||
.spyOn(console, "error")
|
||||
.mockImplementation(() => {});
|
||||
const expiredToken = jwt.sign(
|
||||
{ sub: 1, username: "test", role: "user" },
|
||||
config.jwt.secret,
|
||||
{ expiresIn: -1 },
|
||||
);
|
||||
const result = await verifyAccessToken(expiredToken);
|
||||
expect(result).toBeNull();
|
||||
expect(consoleSpy).toHaveBeenCalled();
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("hashToken", () => {
|
||||
it("produces deterministic SHA-256 hex output", () => {
|
||||
const t1 = hashToken("hello");
|
||||
const t2 = hashToken("hello");
|
||||
expect(t1).toBe(t2);
|
||||
expect(t1).toMatch(/^[a-f0-9]{64}$/);
|
||||
});
|
||||
|
||||
it("produces different hashes for different inputs", () => {
|
||||
const t1 = hashToken("a");
|
||||
const t2 = hashToken("b");
|
||||
expect(t1).not.toBe(t2);
|
||||
});
|
||||
});
|
||||
});
|
||||
19
src/__tests__/customers.schema.test.ts
Normal file
19
src/__tests__/customers.schema.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { UpdateCustomerSchema } from "../schemas/customers.schema";
|
||||
|
||||
describe("UpdateCustomerSchema", () => {
|
||||
it("rejects empty name", () => {
|
||||
const result = UpdateCustomerSchema.safeParse({ name: "" });
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts valid name", () => {
|
||||
const result = UpdateCustomerSchema.safeParse({ name: "Acme Corp" });
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts partial updates without name", () => {
|
||||
const result = UpdateCustomerSchema.safeParse({ street: "Main St" });
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
34
src/__tests__/env.test.ts
Normal file
34
src/__tests__/env.test.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { config } from "../config/env";
|
||||
|
||||
describe("env validation", () => {
|
||||
it("has numeric port within valid range", () => {
|
||||
expect(typeof config.port).toBe("number");
|
||||
expect(config.port).toBeGreaterThan(0);
|
||||
expect(config.port).toBeLessThanOrEqual(65535);
|
||||
});
|
||||
|
||||
it("has JWT_SECRET defined", () => {
|
||||
expect(config.jwt.secret).toBeTruthy();
|
||||
expect(config.jwt.secret.length).toBeGreaterThanOrEqual(32);
|
||||
});
|
||||
|
||||
it("has TOTP_ENCRYPTION_KEY defined", () => {
|
||||
expect(config.totp.encryptionKey).toBeTruthy();
|
||||
expect(config.totp.encryptionKey.length).toBeGreaterThanOrEqual(32);
|
||||
});
|
||||
|
||||
it("has positive JWT expiry values", () => {
|
||||
expect(config.jwt.accessTokenExpiry).toBeGreaterThan(0);
|
||||
expect(config.jwt.refreshTokenSessionExpiry).toBeGreaterThan(0);
|
||||
expect(config.jwt.refreshTokenRememberExpiry).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("has positive maxUploadSize", () => {
|
||||
expect(config.nas.maxUploadSize).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("has DATABASE_URL defined", () => {
|
||||
expect(config.db.url).toBeTruthy();
|
||||
});
|
||||
});
|
||||
64
src/__tests__/exchange-rates.test.ts
Normal file
64
src/__tests__/exchange-rates.test.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { toCzk, getRate } from "../services/exchange-rates";
|
||||
|
||||
// Mock global fetch
|
||||
const mockFetch = vi.fn();
|
||||
global.fetch = mockFetch;
|
||||
|
||||
describe("exchange-rates", () => {
|
||||
beforeEach(() => {
|
||||
mockFetch.mockReset();
|
||||
});
|
||||
|
||||
describe("toCzk", () => {
|
||||
it("returns amount unchanged for CZK", async () => {
|
||||
const result = await toCzk(123.45, "CZK");
|
||||
expect(result).toBe(123.45);
|
||||
});
|
||||
|
||||
it("throws for unknown currency when API fails and no cache", async () => {
|
||||
mockFetch.mockRejectedValue(new Error("Network error"));
|
||||
await expect(toCzk(100, "XYZ")).rejects.toThrow(
|
||||
/Nepodařilo se získat aktuální kurzy/,
|
||||
);
|
||||
});
|
||||
|
||||
it("throws for unknown currency even when API succeeds", async () => {
|
||||
mockFetch.mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
rates: [{ currencyCode: "EUR", rate: 25, amount: 1 }],
|
||||
}),
|
||||
});
|
||||
await expect(toCzk(100, "XYZ")).rejects.toThrow(/Neznámá měna: XYZ/);
|
||||
});
|
||||
|
||||
it("converts EUR using fetched rate", async () => {
|
||||
mockFetch.mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
rates: [{ currencyCode: "EUR", rate: 25, amount: 1 }],
|
||||
}),
|
||||
});
|
||||
const result = await toCzk(100, "EUR");
|
||||
expect(result).toBe(2500);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getRate", () => {
|
||||
it("returns 1 for CZK", async () => {
|
||||
const result = await getRate("CZK");
|
||||
expect(result).toBe(1);
|
||||
});
|
||||
|
||||
it("throws for unknown currency", async () => {
|
||||
mockFetch.mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
rates: [{ currencyCode: "EUR", rate: 25, amount: 1 }],
|
||||
}),
|
||||
});
|
||||
await expect(getRate("XYZ")).rejects.toThrow(/Neznámá měna: XYZ/);
|
||||
});
|
||||
});
|
||||
});
|
||||
55
src/__tests__/invoices.service.test.ts
Normal file
55
src/__tests__/invoices.service.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { invoiceTotalWithVat } from "../services/invoices.service";
|
||||
|
||||
describe("invoiceTotalWithVat", () => {
|
||||
it("calculates subtotal without VAT when apply_vat is false", () => {
|
||||
const inv = {
|
||||
apply_vat: false,
|
||||
vat_rate: { toNumber: () => 21 },
|
||||
currency: "CZK",
|
||||
invoice_items: [
|
||||
{
|
||||
quantity: { toNumber: () => 2 },
|
||||
unit_price: { toNumber: () => 100 },
|
||||
vat_rate: { toNumber: () => 21 },
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(invoiceTotalWithVat(inv)).toBe(200);
|
||||
});
|
||||
|
||||
it("rounds each line VAT to 2 decimals before accumulation", () => {
|
||||
// 3 items @ 33.33 with 21% VAT
|
||||
// Line VAT = 33.33 * 0.21 = 6.9993 -> rounded to 7.00
|
||||
// Total VAT = 7.00 * 3 = 21.00
|
||||
// Subtotal = 33.33 * 3 = 99.99
|
||||
// Total = 99.99 + 21.00 = 120.99
|
||||
const inv = {
|
||||
apply_vat: true,
|
||||
vat_rate: { toNumber: () => 21 },
|
||||
currency: "CZK",
|
||||
invoice_items: Array.from({ length: 3 }, () => ({
|
||||
quantity: { toNumber: () => 1 },
|
||||
unit_price: { toNumber: () => 33.33 },
|
||||
vat_rate: { toNumber: () => 21 },
|
||||
})),
|
||||
};
|
||||
expect(invoiceTotalWithVat(inv)).toBe(120.99);
|
||||
});
|
||||
|
||||
it("handles null quantity and unit_price gracefully", () => {
|
||||
const inv = {
|
||||
apply_vat: true,
|
||||
vat_rate: { toNumber: () => 21 },
|
||||
currency: "CZK",
|
||||
invoice_items: [
|
||||
{
|
||||
quantity: { toNumber: () => 0 },
|
||||
unit_price: { toNumber: () => 0 },
|
||||
vat_rate: { toNumber: () => 21 },
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(invoiceTotalWithVat(inv)).toBe(0);
|
||||
});
|
||||
});
|
||||
55
src/__tests__/nas-file-manager.test.ts
Normal file
55
src/__tests__/nas-file-manager.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { NasFileManager } from "../services/nas-file-manager";
|
||||
|
||||
describe("NasFileManager path traversal", () => {
|
||||
const nas = new NasFileManager();
|
||||
|
||||
describe("deleteItem", () => {
|
||||
it("rejects empty path", async () => {
|
||||
const result = await nas.deleteItem("PRJ-001", "");
|
||||
expect(result).toContain("kořenovou složku");
|
||||
});
|
||||
|
||||
it("rejects root path /", async () => {
|
||||
const result = await nas.deleteItem("PRJ-001", "/");
|
||||
expect(result).toContain("kořenovou složku");
|
||||
});
|
||||
|
||||
it("rejects current directory .", async () => {
|
||||
const result = await nas.deleteItem("PRJ-001", ".");
|
||||
expect(result).toContain("kořenovou složku");
|
||||
});
|
||||
|
||||
it("rejects current directory ./", async () => {
|
||||
const result = await nas.deleteItem("PRJ-001", "./");
|
||||
expect(result).toContain("kořenovou složku");
|
||||
});
|
||||
|
||||
it("rejects path traversal ..", async () => {
|
||||
const result = await nas.deleteItem("PRJ-001", "../etc/passwd");
|
||||
expect(result).toContain("Neplatná cesta");
|
||||
});
|
||||
});
|
||||
|
||||
describe("moveItem", () => {
|
||||
it("rejects empty fromPath", async () => {
|
||||
const result = await nas.moveItem("PRJ-001", "", "dest");
|
||||
expect(result).toContain("kořenovou složku");
|
||||
});
|
||||
|
||||
it("rejects root fromPath /", async () => {
|
||||
const result = await nas.moveItem("PRJ-001", "/", "dest");
|
||||
expect(result).toContain("kořenovou složku");
|
||||
});
|
||||
|
||||
it("rejects current directory .", async () => {
|
||||
const result = await nas.moveItem("PRJ-001", ".", "dest");
|
||||
expect(result).toContain("kořenovou složku");
|
||||
});
|
||||
|
||||
it("rejects path traversal in fromPath", async () => {
|
||||
const result = await nas.moveItem("PRJ-001", "../secret", "dest");
|
||||
expect(result).toContain("Neplatná cesta");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +1,50 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import {
|
||||
generateSharedNumber,
|
||||
generateOfferNumber,
|
||||
} from "../services/numbering.service";
|
||||
import prisma from "../config/database";
|
||||
|
||||
describe("generateSharedNumber", () => {
|
||||
it("returns correct format (YYtypeCode + 4 digits)", async () => {
|
||||
beforeEach(async () => {
|
||||
await prisma.number_sequences.deleteMany({ where: { type: "shared" } });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await prisma.number_sequences.deleteMany({ where: { type: "shared" } });
|
||||
});
|
||||
|
||||
it("returns a non-empty string", async () => {
|
||||
const num = await generateSharedNumber();
|
||||
const yy = String(new Date().getFullYear()).slice(-2);
|
||||
expect(num).toMatch(new RegExp(`^${yy}\\d{2,}\\d{4}$`));
|
||||
expect(typeof num).toBe("string");
|
||||
expect(num.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("increments on consecutive calls", async () => {
|
||||
const num1 = await generateSharedNumber();
|
||||
const num2 = await generateSharedNumber();
|
||||
expect(num1).not.toBe(num2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("generateOfferNumber", () => {
|
||||
it("returns correct format (YEAR/PREFIX/NNN)", async () => {
|
||||
beforeEach(async () => {
|
||||
await prisma.number_sequences.deleteMany({ where: { type: "offer" } });
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await prisma.number_sequences.deleteMany({ where: { type: "offer" } });
|
||||
});
|
||||
|
||||
it("returns a non-empty string", async () => {
|
||||
const num = await generateOfferNumber();
|
||||
const year = new Date().getFullYear();
|
||||
expect(num).toMatch(new RegExp(`^${year}/[A-Z]+/\\d{3,}$`));
|
||||
expect(typeof num).toBe("string");
|
||||
expect(num.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("increments on consecutive calls", async () => {
|
||||
const num1 = await generateOfferNumber();
|
||||
const num2 = await generateOfferNumber();
|
||||
expect(num1).not.toBe(num2);
|
||||
});
|
||||
});
|
||||
|
||||
57
src/__tests__/schema-nan.test.ts
Normal file
57
src/__tests__/schema-nan.test.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { CreateOrderSchema } from "../schemas/orders.schema";
|
||||
import { CreateQuotationSchema } from "../schemas/offers.schema";
|
||||
|
||||
describe("Zod NaN rejection", () => {
|
||||
describe("CreateOrderSchema", () => {
|
||||
it("rejects NaN string in quantity", () => {
|
||||
const result = CreateOrderSchema.safeParse({
|
||||
customer_id: 1,
|
||||
items: [{ quantity: "not-a-number" }],
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects NaN string in unit_price", () => {
|
||||
const result = CreateOrderSchema.safeParse({
|
||||
customer_id: 1,
|
||||
items: [{ unit_price: "abc" }],
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts valid numbers", () => {
|
||||
const result = CreateOrderSchema.safeParse({
|
||||
customer_id: 1,
|
||||
items: [{ quantity: 2, unit_price: 100 }],
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("CreateQuotationSchema", () => {
|
||||
it("rejects NaN string in top-level vat_rate", () => {
|
||||
const result = CreateQuotationSchema.safeParse({
|
||||
customer_id: 1,
|
||||
vat_rate: "bad",
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts valid vat_rate", () => {
|
||||
const result = CreateQuotationSchema.safeParse({
|
||||
customer_id: 1,
|
||||
vat_rate: 21,
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects NaN string in item quantity", () => {
|
||||
const result = CreateQuotationSchema.safeParse({
|
||||
customer_id: 1,
|
||||
items: [{ quantity: "bad" }],
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,9 @@
|
||||
import { lazy, Suspense } from "react";
|
||||
import { Routes, Route } from "react-router-dom";
|
||||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
import { AuthProvider } from "./context/AuthContext";
|
||||
import { AlertProvider } from "./context/AlertContext";
|
||||
import { queryClient } from "./lib/queryClient";
|
||||
import ErrorBoundary from "./components/ErrorBoundary";
|
||||
import AdminLayout from "./components/AdminLayout";
|
||||
import AlertContainer from "./components/AlertContainer";
|
||||
@@ -14,8 +16,8 @@ import "./buttons.css";
|
||||
import "./layout.css";
|
||||
import "./components.css";
|
||||
import "./tables.css";
|
||||
import "./skeleton.css";
|
||||
import "./datepicker.css";
|
||||
import "./bones/registry";
|
||||
import "./filemanager.css";
|
||||
import "./pagination.css";
|
||||
import "./responsive.css";
|
||||
@@ -46,7 +48,6 @@ const OffersTemplates = lazy(() => import("./pages/OffersTemplates"));
|
||||
const Orders = lazy(() => import("./pages/Orders"));
|
||||
const OrderDetail = lazy(() => import("./pages/OrderDetail"));
|
||||
const Projects = lazy(() => import("./pages/Projects"));
|
||||
const ProjectCreate = lazy(() => import("./pages/ProjectCreate"));
|
||||
const ProjectDetail = lazy(() => import("./pages/ProjectDetail"));
|
||||
const Invoices = lazy(() => import("./pages/Invoices"));
|
||||
const InvoiceDetail = lazy(() => import("./pages/InvoiceDetail"));
|
||||
@@ -58,64 +59,80 @@ export default function AdminApp() {
|
||||
return (
|
||||
<AuthProvider>
|
||||
<AlertProvider>
|
||||
<AlertContainer />
|
||||
<ErrorBoundary>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="admin-loading">
|
||||
<div className="admin-spinner" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Routes>
|
||||
<Route path="login" element={<Login />} />
|
||||
<Route element={<AdminLayout />}>
|
||||
<Route index element={<Dashboard />} />
|
||||
<Route path="users" element={<Users />} />
|
||||
<Route path="attendance" element={<Attendance />} />
|
||||
<Route
|
||||
path="attendance/history"
|
||||
element={<AttendanceHistory />}
|
||||
/>
|
||||
<Route path="attendance/admin" element={<AttendanceAdmin />} />
|
||||
<Route
|
||||
path="attendance/balances"
|
||||
element={<AttendanceBalances />}
|
||||
/>
|
||||
<Route path="attendance/requests" element={<LeaveRequests />} />
|
||||
<Route path="attendance/approval" element={<LeaveApproval />} />
|
||||
<Route
|
||||
path="attendance/create"
|
||||
element={<AttendanceCreate />}
|
||||
/>
|
||||
<Route
|
||||
path="attendance/location/:id"
|
||||
element={<AttendanceLocation />}
|
||||
/>
|
||||
<Route path="trips" element={<Trips />} />
|
||||
<Route path="trips/history" element={<TripsHistory />} />
|
||||
<Route path="trips/admin" element={<TripsAdmin />} />
|
||||
<Route path="vehicles" element={<Vehicles />} />
|
||||
<Route path="offers" element={<Offers />} />
|
||||
<Route path="offers/new" element={<OfferDetail />} />
|
||||
<Route path="offers/:id" element={<OfferDetail />} />
|
||||
<Route path="offers/customers" element={<OffersCustomers />} />
|
||||
<Route path="offers/templates" element={<OffersTemplates />} />
|
||||
<Route path="orders" element={<Orders />} />
|
||||
<Route path="orders/:id" element={<OrderDetail />} />
|
||||
<Route path="projects" element={<Projects />} />
|
||||
<Route path="projects/new" element={<ProjectCreate />} />
|
||||
<Route path="projects/:id" element={<ProjectDetail />} />
|
||||
<Route path="invoices" element={<Invoices />} />
|
||||
<Route path="invoices/new" element={<InvoiceDetail />} />
|
||||
<Route path="invoices/:id" element={<InvoiceDetail />} />
|
||||
<Route path="settings" element={<Settings />} />
|
||||
<Route path="audit-log" element={<AuditLog />} />
|
||||
</Route>
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AlertContainer />
|
||||
<ErrorBoundary>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="admin-loading">
|
||||
<div className="admin-spinner" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Routes>
|
||||
<Route path="login" element={<Login />} />
|
||||
<Route element={<AdminLayout />}>
|
||||
<Route index element={<Dashboard />} />
|
||||
<Route path="users" element={<Users />} />
|
||||
<Route path="attendance" element={<Attendance />} />
|
||||
<Route
|
||||
path="attendance/history"
|
||||
element={<AttendanceHistory />}
|
||||
/>
|
||||
<Route
|
||||
path="attendance/admin"
|
||||
element={<AttendanceAdmin />}
|
||||
/>
|
||||
<Route
|
||||
path="attendance/balances"
|
||||
element={<AttendanceBalances />}
|
||||
/>
|
||||
<Route
|
||||
path="attendance/requests"
|
||||
element={<LeaveRequests />}
|
||||
/>
|
||||
<Route
|
||||
path="attendance/approval"
|
||||
element={<LeaveApproval />}
|
||||
/>
|
||||
<Route
|
||||
path="attendance/create"
|
||||
element={<AttendanceCreate />}
|
||||
/>
|
||||
<Route
|
||||
path="attendance/location/:id"
|
||||
element={<AttendanceLocation />}
|
||||
/>
|
||||
<Route path="trips" element={<Trips />} />
|
||||
<Route path="trips/history" element={<TripsHistory />} />
|
||||
<Route path="trips/admin" element={<TripsAdmin />} />
|
||||
<Route path="vehicles" element={<Vehicles />} />
|
||||
<Route path="offers" element={<Offers />} />
|
||||
<Route path="offers/new" element={<OfferDetail />} />
|
||||
<Route path="offers/:id" element={<OfferDetail />} />
|
||||
<Route
|
||||
path="offers/customers"
|
||||
element={<OffersCustomers />}
|
||||
/>
|
||||
<Route
|
||||
path="offers/templates"
|
||||
element={<OffersTemplates />}
|
||||
/>
|
||||
<Route path="orders" element={<Orders />} />
|
||||
<Route path="orders/:id" element={<OrderDetail />} />
|
||||
<Route path="projects" element={<Projects />} />
|
||||
<Route path="projects/:id" element={<ProjectDetail />} />
|
||||
<Route path="invoices" element={<Invoices />} />
|
||||
<Route path="invoices/new" element={<InvoiceDetail />} />
|
||||
<Route path="invoices/:id" element={<InvoiceDetail />} />
|
||||
<Route path="settings" element={<Settings />} />
|
||||
<Route path="audit-log" element={<AuditLog />} />
|
||||
</Route>
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</ErrorBoundary>
|
||||
</QueryClientProvider>
|
||||
</AlertProvider>
|
||||
</AuthProvider>
|
||||
);
|
||||
|
||||
@@ -330,15 +330,6 @@ img {
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
background-position: -200% 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 200% 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Additional Utilities ─────────────────────────────────────────── */
|
||||
|
||||
/* Font sizes */
|
||||
|
||||
1142
src/admin/bones/attendance-balances.bones.json
Normal file
1142
src/admin/bones/attendance-balances.bones.json
Normal file
File diff suppressed because it is too large
Load Diff
263
src/admin/bones/attendance-create.bones.json
Normal file
263
src/admin/bones/attendance-create.bones.json
Normal file
@@ -0,0 +1,263 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
1094
src/admin/bones/attendance-history-fund.bones.json
Normal file
1094
src/admin/bones/attendance-history-fund.bones.json
Normal file
File diff suppressed because it is too large
Load Diff
1094
src/admin/bones/attendance-history-table.bones.json
Normal file
1094
src/admin/bones/attendance-history-table.bones.json
Normal file
File diff suppressed because it is too large
Load Diff
1149
src/admin/bones/audit-log-rows.bones.json
Normal file
1149
src/admin/bones/audit-log-rows.bones.json
Normal file
File diff suppressed because it is too large
Load Diff
506
src/admin/bones/dash-sessions.bones.json
Normal file
506
src/admin/bones/dash-sessions.bones.json
Normal file
@@ -0,0 +1,506 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
707
src/admin/bones/invoice-detail.bones.json
Normal file
707
src/admin/bones/invoice-detail.bones.json
Normal file
@@ -0,0 +1,707 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
599
src/admin/bones/leave-approval.bones.json
Normal file
599
src/admin/bones/leave-approval.bones.json
Normal file
@@ -0,0 +1,599 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
704
src/admin/bones/leave-requests.bones.json
Normal file
704
src/admin/bones/leave-requests.bones.json
Normal file
@@ -0,0 +1,704 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
620
src/admin/bones/offer-detail.bones.json
Normal file
620
src/admin/bones/offer-detail.bones.json
Normal file
@@ -0,0 +1,620 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
641
src/admin/bones/offers-customers.bones.json
Normal file
641
src/admin/bones/offers-customers.bones.json
Normal file
@@ -0,0 +1,641 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
452
src/admin/bones/offers-templates.bones.json
Normal file
452
src/admin/bones/offers-templates.bones.json
Normal file
@@ -0,0 +1,452 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
872
src/admin/bones/offers.bones.json
Normal file
872
src/admin/bones/offers.bones.json
Normal file
@@ -0,0 +1,872 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
998
src/admin/bones/orders.bones.json
Normal file
998
src/admin/bones/orders.bones.json
Normal file
@@ -0,0 +1,998 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
371
src/admin/bones/project-detail.bones.json
Normal file
371
src/admin/bones/project-detail.bones.json
Normal file
@@ -0,0 +1,371 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
746
src/admin/bones/projects.bones.json
Normal file
746
src/admin/bones/projects.bones.json
Normal file
@@ -0,0 +1,746 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
50
src/admin/bones/registry.ts
Normal file
50
src/admin/bones/registry.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
"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,
|
||||
})
|
||||
725
src/admin/bones/trips-admin.bones.json
Normal file
725
src/admin/bones/trips-admin.bones.json
Normal file
@@ -0,0 +1,725 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
725
src/admin/bones/trips-history.bones.json
Normal file
725
src/admin/bones/trips-history.bones.json
Normal file
@@ -0,0 +1,725 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
767
src/admin/bones/users.bones.json
Normal file
767
src/admin/bones/users.bones.json
Normal file
@@ -0,0 +1,767 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
746
src/admin/bones/vehicles.bones.json
Normal file
746
src/admin/bones/vehicles.bones.json
Normal file
@@ -0,0 +1,746 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -75,6 +75,19 @@ function NativeInput({
|
||||
disabled,
|
||||
}: NativeInputProps) {
|
||||
const type = modeToInputType[mode] || "date";
|
||||
// For time inputs, min/max must be in HH:mm format, not date format
|
||||
const formatTimeMinMax = (val: string | undefined): string | undefined => {
|
||||
if (!val) return undefined;
|
||||
// If it looks like a date string (yyyy-MM-dd), extract time portion if present,
|
||||
// otherwise it's not a valid time min/max — return undefined
|
||||
if (val.includes("T")) return val.split("T")[1]?.substring(0, 5);
|
||||
if (val.includes(":")) return val.substring(0, 5);
|
||||
return undefined;
|
||||
};
|
||||
const minProp =
|
||||
mode === "time" ? formatTimeMinMax(minDate) : minDate || undefined;
|
||||
const maxProp =
|
||||
mode === "time" ? formatTimeMinMax(maxDate) : maxDate || undefined;
|
||||
return (
|
||||
<input
|
||||
type={type}
|
||||
@@ -84,14 +97,14 @@ function NativeInput({
|
||||
className="admin-form-input"
|
||||
required={required}
|
||||
disabled={disabled}
|
||||
min={minDate || undefined}
|
||||
max={maxDate || undefined}
|
||||
min={minProp}
|
||||
max={maxProp}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
interface AdminDatePickerProps {
|
||||
mode?: "date" | "month" | "datetime" | "time";
|
||||
mode?: "date" | "month" | "time";
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
minDate?: string;
|
||||
@@ -165,17 +178,22 @@ export default function AdminDatePicker({
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const commonProps = {
|
||||
selected: toDate(value),
|
||||
onChange: handleChange,
|
||||
locale: "cs",
|
||||
customInput: (
|
||||
const customInput = useMemo(
|
||||
() => (
|
||||
<CustomInput
|
||||
required={required}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
/>
|
||||
),
|
||||
[required, placeholder, disabled],
|
||||
);
|
||||
|
||||
const commonProps = {
|
||||
selected: toDate(value),
|
||||
onChange: handleChange,
|
||||
locale: "cs",
|
||||
customInput,
|
||||
minDate: parseMinMax(minDate),
|
||||
maxDate: parseMinMax(maxDate),
|
||||
popperPlacement: "bottom-start" as const,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
formatDate,
|
||||
formatDatetime,
|
||||
@@ -64,21 +64,30 @@ function renderProjectCell(record: AttendanceRecord): React.ReactNode {
|
||||
let h: number,
|
||||
m: number,
|
||||
isActive = false;
|
||||
let durationValid = true;
|
||||
if (log.hours !== null && log.hours !== undefined) {
|
||||
h = parseInt(String(log.hours)) || 0;
|
||||
m = parseInt(String(log.minutes)) || 0;
|
||||
} else {
|
||||
isActive = !log.ended_at;
|
||||
const end = log.ended_at ? new Date(log.ended_at) : new Date();
|
||||
const mins = Math.floor(
|
||||
(end.getTime() - new Date(log.started_at!).getTime()) / 60000,
|
||||
);
|
||||
h = Math.floor(mins / 60);
|
||||
m = mins % 60;
|
||||
const start = log.started_at ? new Date(log.started_at) : null;
|
||||
if (start && !isNaN(start.getTime()) && !isNaN(end.getTime())) {
|
||||
const mins = Math.max(
|
||||
0,
|
||||
Math.floor((end.getTime() - start.getTime()) / 60000),
|
||||
);
|
||||
h = Math.floor(mins / 60);
|
||||
m = mins % 60;
|
||||
} else {
|
||||
durationValid = false;
|
||||
h = 0;
|
||||
m = 0;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<span
|
||||
key={log.id || i}
|
||||
key={log.id ?? i}
|
||||
className="admin-badge"
|
||||
style={{
|
||||
fontSize: "0.7rem",
|
||||
@@ -86,8 +95,10 @@ function renderProjectCell(record: AttendanceRecord): React.ReactNode {
|
||||
background: isActive ? "var(--accent-light)" : undefined,
|
||||
}}
|
||||
>
|
||||
{log.project_name || `#${log.project_id}`} ({h}:
|
||||
{String(m).padStart(2, "0")}h{isActive ? " \u25B8" : ""})
|
||||
{log.project_name || `#${log.project_id}`}{" "}
|
||||
{durationValid
|
||||
? `(${h}:${String(m).padStart(2, "0")}h${isActive ? " \u25B8" : ""})`
|
||||
: "—"}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
@@ -143,7 +154,8 @@ export default function AttendanceShiftTable({
|
||||
const leaveType = record.leave_type || "work";
|
||||
const isLeave = leaveType !== "work";
|
||||
const workMinutes = isLeave
|
||||
? (Number(record.leave_hours) || 8) * 60
|
||||
? (record.leave_hours != null ? Number(record.leave_hours) : 8) *
|
||||
60
|
||||
: calculateWorkMinutes(record);
|
||||
const hasLocation =
|
||||
(record.arrival_lat && record.arrival_lng) ||
|
||||
@@ -183,7 +195,7 @@ export default function AttendanceShiftTable({
|
||||
title="Zobrazit polohu"
|
||||
aria-label="Zobrazit polohu"
|
||||
>
|
||||
{"\uD83D\uDCCD"}
|
||||
<span aria-hidden="true">{"\uD83D\uDCCD"}</span>
|
||||
</Link>
|
||||
) : (
|
||||
"\u2014"
|
||||
|
||||
@@ -17,7 +17,7 @@ interface BulkAttendanceUser {
|
||||
}
|
||||
|
||||
interface BulkAttendanceModalProps {
|
||||
show: boolean;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
form: BulkAttendanceForm;
|
||||
setForm: (form: BulkAttendanceForm) => void;
|
||||
@@ -29,7 +29,7 @@ interface BulkAttendanceModalProps {
|
||||
}
|
||||
|
||||
export default function BulkAttendanceModal({
|
||||
show,
|
||||
isOpen,
|
||||
onClose,
|
||||
form,
|
||||
setForm,
|
||||
@@ -39,11 +39,11 @@ export default function BulkAttendanceModal({
|
||||
toggleUser,
|
||||
toggleAllUsers,
|
||||
}: BulkAttendanceModalProps) {
|
||||
useModalLock(show);
|
||||
useModalLock(isOpen);
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{show && (
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
className="admin-modal-overlay"
|
||||
initial={{ opacity: 0 }}
|
||||
@@ -57,13 +57,21 @@ export default function BulkAttendanceModal({
|
||||
/>
|
||||
<motion.div
|
||||
className="admin-modal admin-modal-lg"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="bulk-attendance-modal-title"
|
||||
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 className="admin-modal-title">Vyplnit docházku za měsíc</h2>
|
||||
<h2
|
||||
id="bulk-attendance-modal-title"
|
||||
className="admin-modal-title"
|
||||
>
|
||||
Vyplnit docházku za měsíc
|
||||
</h2>
|
||||
<p
|
||||
style={{
|
||||
color: "var(--text-secondary)",
|
||||
|
||||
@@ -14,6 +14,71 @@ interface ConfirmModalProps {
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
function ConfirmIcon({ type }: { type: string }) {
|
||||
switch (type) {
|
||||
case "danger":
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<line x1="15" y1="9" x2="9" y2="15" />
|
||||
<line x1="9" y1="9" x2="15" y2="15" />
|
||||
</svg>
|
||||
);
|
||||
case "info":
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<line x1="12" y1="16" x2="12" y2="12" />
|
||||
<line x1="12" y1="8" x2="12.01" y2="8" />
|
||||
</svg>
|
||||
);
|
||||
case "warning":
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
||||
<line x1="12" y1="9" x2="12" y2="13" />
|
||||
<line x1="12" y1="17" x2="12.01" y2="17" />
|
||||
</svg>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<line x1="12" y1="16" x2="12" y2="12" />
|
||||
<line x1="12" y1="8" x2="12.01" y2="8" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function ConfirmModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
@@ -39,6 +104,9 @@ export default function ConfirmModal({
|
||||
<div className="admin-modal-backdrop" onClick={onClose} />
|
||||
<motion.div
|
||||
className="admin-modal admin-confirm-modal"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="confirm-modal-title"
|
||||
initial={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95, y: 20 }}
|
||||
@@ -46,20 +114,11 @@ export default function ConfirmModal({
|
||||
>
|
||||
<div className="admin-modal-body admin-confirm-content">
|
||||
<div className={`admin-confirm-icon admin-confirm-icon-${type}`}>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
||||
<line x1="12" y1="9" x2="12" y2="13" />
|
||||
<line x1="12" y1="17" x2="12.01" y2="17" />
|
||||
</svg>
|
||||
<ConfirmIcon type={type} />
|
||||
</div>
|
||||
<h2 className="admin-confirm-title">{title}</h2>
|
||||
<h2 id="confirm-modal-title" className="admin-confirm-title">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="admin-confirm-message">{message}</p>
|
||||
</div>
|
||||
<div className="admin-modal-footer">
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import type { CSSProperties, ReactNode } from "react";
|
||||
import {
|
||||
type CSSProperties,
|
||||
type ReactNode,
|
||||
isValidElement,
|
||||
cloneElement,
|
||||
useId,
|
||||
} from "react";
|
||||
|
||||
interface FormFieldProps {
|
||||
label: ReactNode;
|
||||
@@ -15,13 +21,22 @@ export default function FormField({
|
||||
required,
|
||||
style,
|
||||
}: FormFieldProps) {
|
||||
const generatedId = useId();
|
||||
const childProps = isValidElement(children)
|
||||
? (children.props as Record<string, unknown>)
|
||||
: null;
|
||||
const childId = childProps?.id ? String(childProps.id) : generatedId;
|
||||
const childWithId = isValidElement(children)
|
||||
? cloneElement(children, { id: childId } as React.Attributes)
|
||||
: children;
|
||||
|
||||
return (
|
||||
<div className="admin-form-group" style={style}>
|
||||
<label className="admin-form-label">
|
||||
<label className="admin-form-label" htmlFor={childId}>
|
||||
{label}
|
||||
{required && <span className="admin-form-required"> *</span>}
|
||||
</label>
|
||||
{children}
|
||||
{childWithId}
|
||||
{error && <span className="admin-form-error">{error}</span>}
|
||||
</div>
|
||||
);
|
||||
|
||||
396
src/admin/components/OrderConfirmationModal.tsx
Normal file
396
src/admin/components/OrderConfirmationModal.tsx
Normal file
@@ -0,0 +1,396 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { useAlert } from "../context/AlertContext";
|
||||
|
||||
interface ConfirmationItem {
|
||||
description: string;
|
||||
quantity: number;
|
||||
unit: string;
|
||||
unit_price: number;
|
||||
is_included_in_total: boolean;
|
||||
vat_rate: number;
|
||||
}
|
||||
|
||||
interface OrderConfirmationModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onGenerate: (
|
||||
lang: string,
|
||||
applyVat: boolean,
|
||||
items?: ConfirmationItem[],
|
||||
) => Promise<void>;
|
||||
initialItems: ConfirmationItem[];
|
||||
orderNumber: string;
|
||||
defaultVatRate: number;
|
||||
applyVat: boolean;
|
||||
}
|
||||
|
||||
export default function OrderConfirmationModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
onGenerate,
|
||||
initialItems,
|
||||
orderNumber,
|
||||
defaultVatRate,
|
||||
applyVat,
|
||||
}: OrderConfirmationModalProps) {
|
||||
const alert = useAlert();
|
||||
const [step, setStep] = useState<"choose" | "edit">("choose");
|
||||
const [lang, setLang] = useState<string>("cs");
|
||||
const [applyVatState, setApplyVatState] = useState(applyVat);
|
||||
const [items, setItems] = useState<ConfirmationItem[]>(initialItems);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const handleUseExisting = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await onGenerate(lang, applyVatState, undefined);
|
||||
} catch (err) {
|
||||
console.error("Chyba při generování potvrzení:", err);
|
||||
alert.error("Nepodařilo se vygenerovat potvrzení");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setStep("choose");
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const handleEditGenerate = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await onGenerate(lang, applyVatState, items);
|
||||
} catch (err) {
|
||||
console.error("Chyba při generování potvrzení:", err);
|
||||
alert.error("Nepodařilo se vygenerovat potvrzení");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
setStep("choose");
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
const updateItem = useCallback(
|
||||
(
|
||||
index: number,
|
||||
field: keyof ConfirmationItem,
|
||||
value: string | number | boolean,
|
||||
) => {
|
||||
setItems((prev) => {
|
||||
const next = [...prev];
|
||||
next[index] = { ...next[index], [field]: value };
|
||||
return next;
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const removeItem = useCallback((index: number) => {
|
||||
setItems((prev) => prev.filter((_, i) => i !== index));
|
||||
}, []);
|
||||
|
||||
const addItem = useCallback(() => {
|
||||
setItems((prev) => [
|
||||
...prev,
|
||||
{
|
||||
description: "",
|
||||
quantity: 1,
|
||||
unit: "ks",
|
||||
unit_price: 0,
|
||||
is_included_in_total: true,
|
||||
vat_rate: defaultVatRate,
|
||||
},
|
||||
]);
|
||||
}, [defaultVatRate]);
|
||||
|
||||
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={onClose} />
|
||||
<motion.div
|
||||
className={
|
||||
step === "edit" ? "admin-modal admin-modal-lg" : "admin-modal"
|
||||
}
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="order-confirmation-modal-title"
|
||||
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="order-confirmation-modal-title"
|
||||
className="admin-modal-title"
|
||||
>
|
||||
Potvrzení objednávky {orderNumber}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="admin-modal-body">
|
||||
{step === "choose" ? (
|
||||
<div className="admin-form">
|
||||
<div className="admin-form-group">
|
||||
<label className="admin-form-label">Jazyk dokumentu</label>
|
||||
<div className="flex-row gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLang("cs")}
|
||||
className={
|
||||
lang === "cs"
|
||||
? "admin-btn admin-btn-primary admin-btn-sm"
|
||||
: "admin-btn admin-btn-secondary admin-btn-sm"
|
||||
}
|
||||
>
|
||||
Čeština
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setLang("en")}
|
||||
className={
|
||||
lang === "en"
|
||||
? "admin-btn admin-btn-primary admin-btn-sm"
|
||||
: "admin-btn admin-btn-secondary admin-btn-sm"
|
||||
}
|
||||
>
|
||||
English
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="admin-form-group">
|
||||
<label className="admin-form-label">DPH</label>
|
||||
<div className="flex-row gap-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setApplyVatState(true)}
|
||||
className={
|
||||
applyVatState
|
||||
? "admin-btn admin-btn-primary admin-btn-sm"
|
||||
: "admin-btn admin-btn-secondary admin-btn-sm"
|
||||
}
|
||||
>
|
||||
S DPH
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setApplyVatState(false)}
|
||||
className={
|
||||
!applyVatState
|
||||
? "admin-btn admin-btn-primary admin-btn-sm"
|
||||
: "admin-btn admin-btn-secondary admin-btn-sm"
|
||||
}
|
||||
>
|
||||
Bez DPH
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="admin-form-group">
|
||||
<label className="admin-form-label">Obsah potvrzení</label>
|
||||
<p
|
||||
className="text-secondary"
|
||||
style={{ marginBottom: "0.75rem" }}
|
||||
>
|
||||
Jak chcete připravit potvrzení objednávky?
|
||||
</p>
|
||||
<button
|
||||
onClick={handleUseExisting}
|
||||
disabled={loading}
|
||||
className="admin-btn admin-btn-primary w-full"
|
||||
style={{ marginBottom: "0.5rem" }}
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
Generuji...
|
||||
</>
|
||||
) : (
|
||||
"Použít položky z objednávky"
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
setItems(initialItems.length > 0 ? initialItems : []);
|
||||
setStep("edit");
|
||||
}}
|
||||
disabled={loading}
|
||||
className="admin-btn admin-btn-secondary w-full"
|
||||
>
|
||||
Upravit položky
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="admin-form">
|
||||
<div className="admin-table-responsive">
|
||||
<table className="admin-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Popis</th>
|
||||
<th>Mn.</th>
|
||||
<th>Jedn.</th>
|
||||
<th>Cena</th>
|
||||
<th>%DPH</th>
|
||||
<th style={{ width: "40px" }} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{items.map((item, i) => (
|
||||
<tr key={i}>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
value={item.description}
|
||||
onChange={(e) =>
|
||||
updateItem(i, "description", e.target.value)
|
||||
}
|
||||
className="admin-form-input"
|
||||
style={{ minWidth: "200px" }}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
value={item.quantity}
|
||||
onChange={(e) =>
|
||||
updateItem(
|
||||
i,
|
||||
"quantity",
|
||||
Number(e.target.value) || 0,
|
||||
)
|
||||
}
|
||||
className="admin-form-input"
|
||||
style={{ width: "80px" }}
|
||||
step="0.001"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
value={item.unit}
|
||||
onChange={(e) =>
|
||||
updateItem(i, "unit", e.target.value)
|
||||
}
|
||||
className="admin-form-input"
|
||||
style={{ width: "60px" }}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
value={item.unit_price}
|
||||
onChange={(e) =>
|
||||
updateItem(
|
||||
i,
|
||||
"unit_price",
|
||||
Number(e.target.value) || 0,
|
||||
)
|
||||
}
|
||||
className="admin-form-input"
|
||||
style={{ width: "100px" }}
|
||||
step="0.01"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
value={item.vat_rate}
|
||||
onChange={(e) =>
|
||||
updateItem(
|
||||
i,
|
||||
"vat_rate",
|
||||
Number(e.target.value) || 0,
|
||||
)
|
||||
}
|
||||
className="admin-form-input"
|
||||
style={{ width: "70px" }}
|
||||
step="1"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
onClick={() => removeItem(i)}
|
||||
className="admin-btn-icon danger"
|
||||
title="Odstranit"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
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>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button
|
||||
onClick={addItem}
|
||||
className="admin-btn admin-btn-secondary admin-btn-sm"
|
||||
>
|
||||
+ Přidat položku
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="admin-modal-footer">
|
||||
{step === "edit" && (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setStep("choose")}
|
||||
className="admin-btn admin-btn-secondary"
|
||||
disabled={loading}
|
||||
>
|
||||
Zpět
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleEditGenerate}
|
||||
className="admin-btn admin-btn-primary"
|
||||
disabled={loading || items.length === 0}
|
||||
>
|
||||
{loading ? (
|
||||
<>
|
||||
<div className="admin-spinner admin-spinner-sm" />
|
||||
Generuji...
|
||||
</>
|
||||
) : (
|
||||
"Vygenerovat PDF"
|
||||
)}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{step === "choose" && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="admin-btn admin-btn-secondary"
|
||||
disabled={loading}
|
||||
>
|
||||
Zrušit
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
@@ -36,13 +36,18 @@ export default function Pagination({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="admin-pagination">
|
||||
<div
|
||||
className="admin-pagination"
|
||||
role="navigation"
|
||||
aria-label="Stránkování"
|
||||
>
|
||||
<div className="admin-pagination-info">{total} záznamů</div>
|
||||
<div className="admin-pagination-controls">
|
||||
<button
|
||||
disabled={page <= 1}
|
||||
onClick={() => onPageChange(page - 1)}
|
||||
className="admin-pagination-page"
|
||||
aria-label="Předchozí stránka"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
@@ -65,6 +70,8 @@ export default function Pagination({
|
||||
key={p}
|
||||
onClick={() => onPageChange(p)}
|
||||
className={`admin-pagination-page ${p === page ? "active" : ""}`}
|
||||
aria-label={`Stránka ${p}`}
|
||||
aria-current={p === page ? "page" : undefined}
|
||||
>
|
||||
{p}
|
||||
</button>
|
||||
@@ -74,6 +81,7 @@ export default function Pagination({
|
||||
disabled={page >= total_pages}
|
||||
onClick={() => onPageChange(page + 1)}
|
||||
className="admin-pagination-page"
|
||||
aria-label="Další stránka"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { useState, useEffect, useRef, useCallback } from "react";
|
||||
import { useState, useRef } from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { projectFilesOptions } from "../lib/queries/projects";
|
||||
import { useAlert } from "../context/AlertContext";
|
||||
import ConfirmModal from "./ConfirmModal";
|
||||
import apiFetch from "../utils/api";
|
||||
import { Skeleton } from "boneyard-js/react";
|
||||
import ProjectFileManagerFixture from "../fixtures/ProjectFileManagerFixture";
|
||||
|
||||
const API_BASE = "/api/admin";
|
||||
|
||||
@@ -196,13 +200,11 @@ export default function ProjectFileManager({
|
||||
hasNasFolder,
|
||||
}: ProjectFileManagerProps) {
|
||||
const alert = useAlert();
|
||||
const queryClient = useQueryClient();
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const isCancelling = useRef(false);
|
||||
|
||||
const [items, setItems] = useState<FileItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [currentPath, setCurrentPath] = useState("");
|
||||
const [breadcrumb, setBreadcrumb] = useState<string[]>([""]);
|
||||
const [fullPath, setFullPath] = useState("");
|
||||
|
||||
const [dragOver, setDragOver] = useState(false);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
@@ -216,59 +218,25 @@ export default function ProjectFileManager({
|
||||
|
||||
const [deleteTarget, setDeleteTarget] = useState<FileItem | null>(null);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
|
||||
const canManage = hasPermission("projects.files");
|
||||
|
||||
const fetchFiles = useCallback(
|
||||
async (path = "", options: { ignore?: boolean } = {}) => {
|
||||
setLoading(true);
|
||||
setErrorMessage(null);
|
||||
try {
|
||||
const params = new URLSearchParams({ project_id: String(projectId) });
|
||||
if (path) {
|
||||
params.set("path", path);
|
||||
}
|
||||
const res = await apiFetch(`${API_BASE}/project-files?${params}`);
|
||||
if (options.ignore) return;
|
||||
if (res.status === 401) return;
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
setItems(data.data.items || []);
|
||||
setBreadcrumb(data.data.breadcrumb || [""]);
|
||||
setCurrentPath(data.data.path || "");
|
||||
setFullPath(data.data.full_path || "");
|
||||
} else if (res.status === 404) {
|
||||
setItems([]);
|
||||
setBreadcrumb([""]);
|
||||
} else {
|
||||
setErrorMessage(data.error || "Nepodařilo se načíst soubory");
|
||||
}
|
||||
} catch {
|
||||
if (!options.ignore) {
|
||||
setErrorMessage("Chyba připojení");
|
||||
}
|
||||
} finally {
|
||||
if (!options.ignore) {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
},
|
||||
[projectId],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const opts = { ignore: false };
|
||||
fetchFiles("", opts);
|
||||
return () => {
|
||||
opts.ignore = true;
|
||||
};
|
||||
}, [fetchFiles]);
|
||||
const {
|
||||
data: filesData,
|
||||
isPending: filesLoading,
|
||||
error: filesError,
|
||||
} = useQuery(projectFilesOptions(projectId, currentPath));
|
||||
const items = filesData?.items ?? [];
|
||||
const breadcrumb = filesData?.breadcrumb ?? [""];
|
||||
const fullPath = filesData?.full_path ?? "";
|
||||
const errorMessage = filesError
|
||||
? filesError.message || "Nepodařilo se načíst soubory"
|
||||
: null;
|
||||
|
||||
const navigateTo = (path: string) => {
|
||||
setNewFolderMode(false);
|
||||
setRenamingItem(null);
|
||||
fetchFiles(path);
|
||||
setCurrentPath(path);
|
||||
};
|
||||
|
||||
const handleBreadcrumbClick = (index: number) => {
|
||||
@@ -331,7 +299,9 @@ export default function ProjectFileManager({
|
||||
? "Soubor byl nahrán"
|
||||
: `Nahráno ${successCount} souborů`;
|
||||
alert.success(msg);
|
||||
fetchFiles(currentPath);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["projects", String(projectId), "files"],
|
||||
});
|
||||
}
|
||||
if (errorMsg) {
|
||||
alert.error(errorMsg);
|
||||
@@ -382,7 +352,9 @@ export default function ProjectFileManager({
|
||||
alert.success("Složka byla vytvořena");
|
||||
setNewFolderMode(false);
|
||||
setNewFolderName("");
|
||||
fetchFiles(currentPath);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["projects", String(projectId), "files"],
|
||||
});
|
||||
} else {
|
||||
alert.error(data.error || "Nepodařilo se vytvořit složku");
|
||||
}
|
||||
@@ -443,7 +415,9 @@ export default function ProjectFileManager({
|
||||
? "Složka byla smazána"
|
||||
: "Soubor byl smazán",
|
||||
);
|
||||
fetchFiles(currentPath);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["projects", String(projectId), "files"],
|
||||
});
|
||||
} else {
|
||||
alert.error(data.error || "Nepodařilo se smazat");
|
||||
}
|
||||
@@ -478,7 +452,9 @@ export default function ProjectFileManager({
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
alert.success("Přejmenováno");
|
||||
fetchFiles(currentPath);
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ["projects", String(projectId), "files"],
|
||||
});
|
||||
} else {
|
||||
alert.error(data.error || "Nepodařilo se přejmenovat");
|
||||
}
|
||||
@@ -494,32 +470,15 @@ export default function ProjectFileManager({
|
||||
setRenameValue(item.name);
|
||||
};
|
||||
|
||||
if (loading && items.length === 0 && !errorMessage) {
|
||||
if (filesLoading && items.length === 0 && !errorMessage) {
|
||||
return (
|
||||
<div className="admin-card">
|
||||
<div className="admin-card-body">
|
||||
<h3 className="admin-card-title">Soubory</h3>
|
||||
<div className="admin-skeleton" style={{ padding: 0, gap: "0.5rem" }}>
|
||||
{[0, 1, 2, 3].map((i) => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div
|
||||
className="admin-skeleton-line"
|
||||
style={{
|
||||
width: "18px",
|
||||
height: "18px",
|
||||
borderRadius: "4px",
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="admin-skeleton-line"
|
||||
style={{ width: `${60 + i * 10}%` }}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Skeleton
|
||||
name="project-file-manager"
|
||||
loading={filesLoading && items.length === 0}
|
||||
fixture={<ProjectFileManagerFixture />}
|
||||
>
|
||||
<div />
|
||||
</Skeleton>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -709,7 +668,7 @@ export default function ProjectFileManager({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{items.length === 0 && !loading ? (
|
||||
{items.length === 0 && !filesLoading ? (
|
||||
<div className="fm-empty">
|
||||
<svg
|
||||
width="32"
|
||||
@@ -768,10 +727,26 @@ export default function ProjectFileManager({
|
||||
}}
|
||||
autoFocus
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") handleRename(item);
|
||||
if (e.key === "Escape") setRenamingItem(null);
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
handleRename(item);
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
e.preventDefault();
|
||||
isCancelling.current = true;
|
||||
setRenamingItem(null);
|
||||
setRenameValue(item.name);
|
||||
setTimeout(() => {
|
||||
isCancelling.current = false;
|
||||
}, 0);
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (isCancelling.current) {
|
||||
return;
|
||||
}
|
||||
handleRename(item);
|
||||
}}
|
||||
onBlur={() => handleRename(item)}
|
||||
/>
|
||||
) : (
|
||||
<FileNameCell
|
||||
|
||||
@@ -1,66 +1,7 @@
|
||||
import { useMemo, useRef, useCallback } from "react";
|
||||
import { useMemo, useRef, useCallback, useLayoutEffect } from "react";
|
||||
import ReactQuill from "react-quill-new";
|
||||
import "react-quill-new/dist/quill.snow.css";
|
||||
|
||||
const Quill = ReactQuill.Quill;
|
||||
|
||||
if (!(Quill as any).__bohaRegistered) {
|
||||
const Font = Quill.import("attributors/class/font") as any;
|
||||
Font.whitelist = [
|
||||
"arial",
|
||||
"tahoma",
|
||||
"verdana",
|
||||
"georgia",
|
||||
"times-new-roman",
|
||||
"courier-new",
|
||||
"trebuchet-ms",
|
||||
"impact",
|
||||
"comic-sans-ms",
|
||||
"lucida-console",
|
||||
"palatino-linotype",
|
||||
"garamond",
|
||||
];
|
||||
Quill.register(Font, true);
|
||||
|
||||
const SizeStyle = Quill.import("attributors/style/size") as any;
|
||||
SizeStyle.whitelist = [
|
||||
"8px",
|
||||
"9px",
|
||||
"10px",
|
||||
"11px",
|
||||
"12px",
|
||||
"14px",
|
||||
"16px",
|
||||
"18px",
|
||||
"20px",
|
||||
"24px",
|
||||
"28px",
|
||||
"32px",
|
||||
"36px",
|
||||
"48px",
|
||||
];
|
||||
Quill.register(SizeStyle, true);
|
||||
(Quill as any).__bohaRegistered = true;
|
||||
}
|
||||
|
||||
const Font = Quill.import("attributors/class/font") as any;
|
||||
const SIZE_WHITELIST = [
|
||||
"8px",
|
||||
"9px",
|
||||
"10px",
|
||||
"11px",
|
||||
"12px",
|
||||
"14px",
|
||||
"16px",
|
||||
"18px",
|
||||
"20px",
|
||||
"24px",
|
||||
"28px",
|
||||
"32px",
|
||||
"36px",
|
||||
"48px",
|
||||
];
|
||||
|
||||
const COLORS = [
|
||||
"#000000",
|
||||
"#1a1a1a",
|
||||
@@ -95,8 +36,6 @@ const COLORS = [
|
||||
];
|
||||
|
||||
const TOOLBAR = [
|
||||
[{ font: Font.whitelist }],
|
||||
[{ size: SIZE_WHITELIST }],
|
||||
["bold", "italic", "underline", "strike"],
|
||||
[{ color: COLORS }, { background: COLORS }],
|
||||
[{ list: "ordered" }, { list: "bullet" }],
|
||||
@@ -107,8 +46,6 @@ const TOOLBAR = [
|
||||
];
|
||||
|
||||
const FORMATS = [
|
||||
"font",
|
||||
"size",
|
||||
"bold",
|
||||
"italic",
|
||||
"underline",
|
||||
@@ -159,6 +96,16 @@ export default function RichEditor({
|
||||
[onChange],
|
||||
);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!quillRef.current) return;
|
||||
const editor = quillRef.current.getEditor();
|
||||
editor.format("font", "tahoma");
|
||||
editor.format("size", "14px");
|
||||
// Quill auto-focuses on mount with existing content, which scrolls
|
||||
// the page to the editor. Blur to prevent unwanted scroll.
|
||||
editor.blur();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="admin-rich-editor"
|
||||
|
||||
@@ -68,7 +68,7 @@ interface ProjectLogRowProps {
|
||||
|
||||
export interface ShiftFormModalProps {
|
||||
mode: "create" | "edit";
|
||||
show: boolean;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: () => void;
|
||||
form: ShiftFormData;
|
||||
@@ -196,7 +196,7 @@ function ProjectLogRow({
|
||||
|
||||
export default function ShiftFormModal({
|
||||
mode,
|
||||
show,
|
||||
isOpen,
|
||||
onClose,
|
||||
onSubmit,
|
||||
form,
|
||||
@@ -208,7 +208,7 @@ export default function ShiftFormModal({
|
||||
onShiftDateChange,
|
||||
editingRecord,
|
||||
}: ShiftFormModalProps) {
|
||||
useModalLock(show);
|
||||
useModalLock(isOpen);
|
||||
const isCreate = mode === "create";
|
||||
const isWorkType = form.leave_type === "work";
|
||||
|
||||
@@ -240,7 +240,7 @@ export default function ShiftFormModal({
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{show && (
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
className="admin-modal-overlay"
|
||||
initial={{ opacity: 0 }}
|
||||
@@ -251,13 +251,16 @@ export default function ShiftFormModal({
|
||||
<div className="admin-modal-backdrop" onClick={onClose} />
|
||||
<motion.div
|
||||
className="admin-modal admin-modal-lg"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="shift-form-modal-title"
|
||||
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 className="admin-modal-title">
|
||||
<h2 id="shift-form-modal-title" className="admin-modal-title">
|
||||
{isCreate ? "Přidat záznam docházky" : "Upravit docházku"}
|
||||
</h2>
|
||||
{!isCreate && editingRecord && (
|
||||
|
||||
@@ -1,27 +1,17 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useState } from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { motion } from "framer-motion";
|
||||
import { useAlert } from "../../context/AlertContext";
|
||||
import ConfirmModal from "../ConfirmModal";
|
||||
import useModalLock from "../../hooks/useModalLock";
|
||||
import apiFetch from "../../utils/api";
|
||||
import { formatSessionDate } from "../../utils/dashboardHelpers";
|
||||
import { sessionsOptions, type Session } from "../../lib/queries/dashboard";
|
||||
import { Skeleton } from "boneyard-js/react";
|
||||
import DashSessionsFixture from "../../fixtures/DashSessionsFixture";
|
||||
|
||||
const API_BASE = "/api/admin";
|
||||
|
||||
interface DeviceInfo {
|
||||
icon?: string;
|
||||
browser?: string;
|
||||
os?: string;
|
||||
}
|
||||
|
||||
interface Session {
|
||||
id: number | string;
|
||||
is_current: boolean;
|
||||
device_info?: DeviceInfo;
|
||||
ip_address: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface DeleteModalState {
|
||||
isOpen: boolean;
|
||||
session: Session | null;
|
||||
@@ -77,9 +67,10 @@ function getDeviceIcon(iconType?: string) {
|
||||
|
||||
export default function DashSessions() {
|
||||
const alert = useAlert();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [sessions, setSessions] = useState<Session[]>([]);
|
||||
const [sessionsLoading, setSessionsLoading] = useState(true);
|
||||
const { data: sessions = [], isPending: sessionsLoading } =
|
||||
useQuery(sessionsOptions());
|
||||
const [deleteModal, setDeleteModal] = useState<DeleteModalState>({
|
||||
isOpen: false,
|
||||
session: null,
|
||||
@@ -89,26 +80,6 @@ export default function DashSessions() {
|
||||
|
||||
useModalLock(deleteAllModal);
|
||||
|
||||
const fetchSessions = useCallback(async () => {
|
||||
try {
|
||||
const response = await apiFetch(`${API_BASE}/sessions`);
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
setSessions(
|
||||
Array.isArray(data.data) ? data.data : data.data?.sessions || [],
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
// session fetch failed silently
|
||||
} finally {
|
||||
setSessionsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchSessions();
|
||||
}, [fetchSessions]);
|
||||
|
||||
const handleDeleteSession = async () => {
|
||||
if (!deleteModal.session) {
|
||||
return;
|
||||
@@ -122,7 +93,7 @@ export default function DashSessions() {
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
setDeleteModal({ isOpen: false, session: null });
|
||||
setSessions((prev) => prev.filter((s) => s.id !== sessionId));
|
||||
queryClient.invalidateQueries({ queryKey: ["sessions"] });
|
||||
alert.success("Relace byla ukončena");
|
||||
} else {
|
||||
alert.error(data.error || "Nepodařilo se ukončit relaci");
|
||||
@@ -143,7 +114,7 @@ export default function DashSessions() {
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
setDeleteAllModal(false);
|
||||
setSessions((prev) => prev.filter((s) => s.is_current));
|
||||
queryClient.invalidateQueries({ queryKey: ["sessions"] });
|
||||
alert.success(data.message || "Ostatní relace byly ukončeny");
|
||||
} else {
|
||||
alert.error(data.error || "Nepodařilo se ukončit relace");
|
||||
@@ -183,98 +154,84 @@ export default function DashSessions() {
|
||||
)}
|
||||
</div>
|
||||
<div className="admin-card-body" style={{ padding: 0 }}>
|
||||
{sessionsLoading && (
|
||||
<div
|
||||
className="admin-skeleton"
|
||||
style={{ padding: "1rem", gap: "1rem" }}
|
||||
>
|
||||
{[0, 1, 2].map((i) => (
|
||||
<div key={i} className="admin-skeleton-row">
|
||||
<div className="admin-skeleton-line circle" />
|
||||
<div className="flex-1">
|
||||
<div
|
||||
className="admin-skeleton-line w-1/2"
|
||||
style={{ marginBottom: "0.5rem" }}
|
||||
/>
|
||||
<div
|
||||
className="admin-skeleton-line w-1/3"
|
||||
style={{ height: "10px" }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{!sessionsLoading && sessions.length === 0 && (
|
||||
<div
|
||||
className="text-secondary"
|
||||
style={{
|
||||
padding: "1.5rem",
|
||||
textAlign: "center",
|
||||
fontSize: "0.875rem",
|
||||
}}
|
||||
>
|
||||
Žádné aktivní relace
|
||||
</div>
|
||||
)}
|
||||
{!sessionsLoading && sessions.length > 0 && (
|
||||
<div className="dash-sessions-list">
|
||||
{sessions.map((session) => (
|
||||
<Skeleton
|
||||
name="dash-sessions"
|
||||
loading={sessionsLoading}
|
||||
fixture={<DashSessionsFixture />}
|
||||
>
|
||||
<>
|
||||
{sessions.length === 0 && (
|
||||
<div
|
||||
key={session.id}
|
||||
className={`dash-session-item ${session.is_current ? "dash-session-item-current" : ""}`}
|
||||
className="text-secondary"
|
||||
style={{
|
||||
padding: "1.5rem",
|
||||
textAlign: "center",
|
||||
fontSize: "0.875rem",
|
||||
}}
|
||||
>
|
||||
<div className="dash-session-icon">
|
||||
{getDeviceIcon(session.device_info?.icon)}
|
||||
</div>
|
||||
<div className="dash-session-info">
|
||||
<div className="dash-session-device">
|
||||
{session.device_info?.browser} na{" "}
|
||||
{session.device_info?.os}
|
||||
{session.is_current && (
|
||||
<span
|
||||
className="admin-badge admin-badge-success"
|
||||
style={{ marginLeft: "0.5rem" }}
|
||||
>
|
||||
Aktuální
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="dash-session-meta">
|
||||
<span>{session.ip_address}</span>
|
||||
<span className="dash-session-meta-separator">|</span>
|
||||
<span>{formatSessionDate(session.created_at)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dash-session-actions">
|
||||
{!session.is_current && (
|
||||
<button
|
||||
onClick={() =>
|
||||
setDeleteModal({ isOpen: true, session })
|
||||
}
|
||||
className="admin-btn-icon danger"
|
||||
title="Ukončit relaci"
|
||||
aria-label="Ukončit relaci"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
||||
<polyline points="16 17 21 12 16 7" />
|
||||
<line x1="21" y1="12" x2="9" y2="12" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
Žádné aktivní relace
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
{sessions.length > 0 && (
|
||||
<div className="dash-sessions-list">
|
||||
{sessions.map((session) => (
|
||||
<div
|
||||
key={session.id}
|
||||
className={`dash-session-item ${session.is_current ? "dash-session-item-current" : ""}`}
|
||||
>
|
||||
<div className="dash-session-icon">
|
||||
{getDeviceIcon(session.device_info?.icon)}
|
||||
</div>
|
||||
<div className="dash-session-info">
|
||||
<div className="dash-session-device">
|
||||
{session.device_info?.browser} na{" "}
|
||||
{session.device_info?.os}
|
||||
{session.is_current && (
|
||||
<span
|
||||
className="admin-badge admin-badge-success"
|
||||
style={{ marginLeft: "0.5rem" }}
|
||||
>
|
||||
Aktuální
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="dash-session-meta">
|
||||
<span>{session.ip_address}</span>
|
||||
<span className="dash-session-meta-separator">|</span>
|
||||
<span>{formatSessionDate(session.created_at)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="dash-session-actions">
|
||||
{!session.is_current && (
|
||||
<button
|
||||
onClick={() =>
|
||||
setDeleteModal({ isOpen: true, session })
|
||||
}
|
||||
className="admin-btn-icon danger"
|
||||
title="Ukončit relaci"
|
||||
aria-label="Ukončit relaci"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
||||
<polyline points="16 17 21 12 16 7" />
|
||||
<line x1="21" y1="12" x2="9" y2="12" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
</Skeleton>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
useEffect,
|
||||
type ReactNode,
|
||||
} from "react";
|
||||
|
||||
@@ -39,6 +40,15 @@ export function AlertProvider({ children }: { children: ReactNode }) {
|
||||
}, []);
|
||||
|
||||
const counterRef = useRef(0);
|
||||
const timeoutsRef = useRef<Set<ReturnType<typeof setTimeout>>>(new Set());
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
timeoutsRef.current.forEach(clearTimeout);
|
||||
timeoutsRef.current.clear();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const addAlert = useCallback(
|
||||
(message: string, type = "success", duration = 4000) => {
|
||||
const id = `${Date.now()}-${counterRef.current++}`;
|
||||
@@ -47,7 +57,11 @@ export function AlertProvider({ children }: { children: ReactNode }) {
|
||||
{ id, message, type: type as Alert["type"] },
|
||||
]);
|
||||
if (duration > 0) {
|
||||
setTimeout(() => removeAlert(id), duration);
|
||||
const timeoutId = setTimeout(() => {
|
||||
timeoutsRef.current.delete(timeoutId);
|
||||
removeAlert(id);
|
||||
}, duration);
|
||||
timeoutsRef.current.add(timeoutId);
|
||||
}
|
||||
return id;
|
||||
},
|
||||
|
||||
@@ -84,32 +84,32 @@ function mapUser(u: Record<string, unknown> | null): User | null {
|
||||
} as User;
|
||||
}
|
||||
|
||||
let accessToken: string | null = null;
|
||||
let tokenExpiresAt: number | null = null;
|
||||
let cachedUser: User | null = null;
|
||||
let sessionFetched = false;
|
||||
let silentRefreshInFlight: Promise<boolean> | null = null;
|
||||
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [user, setUser] = useState<User | null>(cachedUser);
|
||||
const [loading, setLoading] = useState(!sessionFetched);
|
||||
const accessTokenRef = useRef<string | null>(null);
|
||||
const tokenExpiresAtRef = useRef<number | null>(null);
|
||||
const cachedUserRef = useRef<User | null>(null);
|
||||
const sessionFetchedRef = useRef(false);
|
||||
const silentRefreshInFlightRef = useRef<Promise<boolean> | null>(null);
|
||||
const hadValidSessionRef = useRef(false);
|
||||
const [user, setUser] = useState<User | null>(cachedUserRef.current);
|
||||
const [loading, setLoading] = useState(!sessionFetchedRef.current);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const refreshTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
cachedUser = user;
|
||||
}, [user]);
|
||||
|
||||
const getAccessTokenFn = useCallback((): string | null => {
|
||||
if (!tokenExpiresAt || Date.now() > tokenExpiresAt - 30000) return null;
|
||||
return accessToken;
|
||||
if (
|
||||
!tokenExpiresAtRef.current ||
|
||||
Date.now() > tokenExpiresAtRef.current - 30000
|
||||
)
|
||||
return null;
|
||||
return accessTokenRef.current;
|
||||
}, []);
|
||||
|
||||
const setAccessTokenFn = useCallback(
|
||||
(token: string | null, expiresIn?: number) => {
|
||||
const ttl = expiresIn ?? 900; // default 15 min matching backend config
|
||||
accessToken = token;
|
||||
tokenExpiresAt = token ? Date.now() + ttl * 1000 : null;
|
||||
accessTokenRef.current = token;
|
||||
tokenExpiresAtRef.current = token ? Date.now() + ttl * 1000 : null;
|
||||
if (refreshTimeoutRef.current) {
|
||||
clearTimeout(refreshTimeoutRef.current);
|
||||
refreshTimeoutRef.current = null;
|
||||
@@ -126,7 +126,8 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const silentRefresh = useCallback(async (): Promise<boolean> => {
|
||||
// Deduplicate concurrent refresh calls — token rotation means only one call can succeed
|
||||
if (silentRefreshInFlight) return silentRefreshInFlight;
|
||||
if (silentRefreshInFlightRef.current)
|
||||
return silentRefreshInFlightRef.current;
|
||||
|
||||
const promise = (async (): Promise<boolean> => {
|
||||
try {
|
||||
@@ -138,23 +139,24 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
if (data.success && data.data?.access_token) {
|
||||
setAccessTokenFn(data.data.access_token, data.data.expires_in);
|
||||
setUser(mapUser(data.data.user));
|
||||
hadValidSessionRef.current = true;
|
||||
return true;
|
||||
}
|
||||
accessToken = null;
|
||||
tokenExpiresAt = null;
|
||||
accessTokenRef.current = null;
|
||||
tokenExpiresAtRef.current = null;
|
||||
setUser(null);
|
||||
cachedUser = null;
|
||||
setSessionExpired();
|
||||
cachedUserRef.current = null;
|
||||
if (hadValidSessionRef.current) setSessionExpired();
|
||||
return false;
|
||||
} catch {
|
||||
// Network error — don't kick the user out, just return false
|
||||
return false;
|
||||
} finally {
|
||||
silentRefreshInFlight = null;
|
||||
silentRefreshInFlightRef.current = null;
|
||||
}
|
||||
})();
|
||||
|
||||
silentRefreshInFlight = promise;
|
||||
silentRefreshInFlightRef.current = promise;
|
||||
return promise;
|
||||
}, [setAccessTokenFn]);
|
||||
|
||||
@@ -172,12 +174,13 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
headers,
|
||||
});
|
||||
if (response.status === 429 || response.status >= 500)
|
||||
return !!cachedUser;
|
||||
return !!cachedUserRef.current;
|
||||
const data = await response.json();
|
||||
if (data.success && data.data?.user) {
|
||||
if (data.data.access_token) setAccessTokenFn(data.data.access_token);
|
||||
setUser(mapUser(data.data.user));
|
||||
cachedUser = mapUser(data.data.user);
|
||||
cachedUserRef.current = mapUser(data.data.user);
|
||||
hadValidSessionRef.current = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -185,15 +188,15 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const refreshed = await silentRefresh();
|
||||
if (refreshed) return true;
|
||||
setUser(null);
|
||||
cachedUser = null;
|
||||
accessToken = null;
|
||||
tokenExpiresAt = null;
|
||||
cachedUserRef.current = null;
|
||||
accessTokenRef.current = null;
|
||||
tokenExpiresAtRef.current = null;
|
||||
return false;
|
||||
} catch {
|
||||
return !!cachedUser;
|
||||
return !!cachedUserRef.current;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
sessionFetched = true;
|
||||
sessionFetchedRef.current = true;
|
||||
}
|
||||
}, [getAccessTokenFn, setAccessTokenFn, silentRefresh]);
|
||||
|
||||
@@ -231,8 +234,9 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
setAccessTokenFn(data.data.access_token, data.data.expires_in);
|
||||
setUser(mapUser(data.data.user));
|
||||
cachedUser = mapUser(data.data.user);
|
||||
sessionFetched = true;
|
||||
cachedUserRef.current = mapUser(data.data.user);
|
||||
sessionFetchedRef.current = true;
|
||||
hadValidSessionRef.current = true;
|
||||
return { success: true };
|
||||
}
|
||||
setError(data.error);
|
||||
@@ -264,14 +268,16 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
login_token: loginToken,
|
||||
totp_code: code,
|
||||
remember_me: remember,
|
||||
isBackup,
|
||||
}),
|
||||
});
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
setAccessTokenFn(data.data.access_token, data.data.expires_in);
|
||||
setUser(mapUser(data.data.user));
|
||||
cachedUser = mapUser(data.data.user);
|
||||
sessionFetched = true;
|
||||
cachedUserRef.current = mapUser(data.data.user);
|
||||
sessionFetchedRef.current = true;
|
||||
hadValidSessionRef.current = true;
|
||||
return { success: true };
|
||||
}
|
||||
setError(data.error);
|
||||
@@ -296,11 +302,12 @@ export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
} catch {
|
||||
/* ignore */
|
||||
} finally {
|
||||
accessToken = null;
|
||||
tokenExpiresAt = null;
|
||||
accessTokenRef.current = null;
|
||||
tokenExpiresAtRef.current = null;
|
||||
setUser(null);
|
||||
cachedUser = null;
|
||||
sessionFetched = false;
|
||||
cachedUserRef.current = null;
|
||||
sessionFetchedRef.current = false;
|
||||
hadValidSessionRef.current = false;
|
||||
if (refreshTimeoutRef.current) {
|
||||
clearTimeout(refreshTimeoutRef.current);
|
||||
refreshTimeoutRef.current = null;
|
||||
|
||||
69
src/admin/fixtures/AttendanceAdminFixture.tsx
Normal file
69
src/admin/fixtures/AttendanceAdminFixture.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
104
src/admin/fixtures/AttendanceBalancesFixture.tsx
Normal file
104
src/admin/fixtures/AttendanceBalancesFixture.tsx
Normal file
@@ -0,0 +1,104 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
43
src/admin/fixtures/AttendanceCreateFixture.tsx
Normal file
43
src/admin/fixtures/AttendanceCreateFixture.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
79
src/admin/fixtures/AttendanceFixture.tsx
Normal file
79
src/admin/fixtures/AttendanceFixture.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
102
src/admin/fixtures/AttendanceHistoryFixture.tsx
Normal file
102
src/admin/fixtures/AttendanceHistoryFixture.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
49
src/admin/fixtures/AttendanceLocationFixture.tsx
Normal file
49
src/admin/fixtures/AttendanceLocationFixture.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
52
src/admin/fixtures/AuditLogFixture.tsx
Normal file
52
src/admin/fixtures/AuditLogFixture.tsx
Normal file
@@ -0,0 +1,52 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
69
src/admin/fixtures/CompanySettingsFixture.tsx
Normal file
69
src/admin/fixtures/CompanySettingsFixture.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
63
src/admin/fixtures/DashSessionsFixture.tsx
Normal file
63
src/admin/fixtures/DashSessionsFixture.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
94
src/admin/fixtures/DashboardFixture.tsx
Normal file
94
src/admin/fixtures/DashboardFixture.tsx
Normal file
@@ -0,0 +1,94 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
86
src/admin/fixtures/InvoiceDetailFixture.tsx
Normal file
86
src/admin/fixtures/InvoiceDetailFixture.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
83
src/admin/fixtures/InvoicesFixture.tsx
Normal file
83
src/admin/fixtures/InvoicesFixture.tsx
Normal file
@@ -0,0 +1,83 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
51
src/admin/fixtures/LeaveApprovalFixture.tsx
Normal file
51
src/admin/fixtures/LeaveApprovalFixture.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
53
src/admin/fixtures/LeaveRequestsFixture.tsx
Normal file
53
src/admin/fixtures/LeaveRequestsFixture.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
60
src/admin/fixtures/OfferDetailFixture.tsx
Normal file
60
src/admin/fixtures/OfferDetailFixture.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
49
src/admin/fixtures/OffersCustomersFixture.tsx
Normal file
49
src/admin/fixtures/OffersCustomersFixture.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
54
src/admin/fixtures/OffersFixture.tsx
Normal file
54
src/admin/fixtures/OffersFixture.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
36
src/admin/fixtures/OffersTemplatesFixture.tsx
Normal file
36
src/admin/fixtures/OffersTemplatesFixture.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
89
src/admin/fixtures/OrderDetailFixture.tsx
Normal file
89
src/admin/fixtures/OrderDetailFixture.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
55
src/admin/fixtures/OrdersFixture.tsx
Normal file
55
src/admin/fixtures/OrdersFixture.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
70
src/admin/fixtures/ProjectDetailFixture.tsx
Normal file
70
src/admin/fixtures/ProjectDetailFixture.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
53
src/admin/fixtures/ProjectFileManagerFixture.tsx
Normal file
53
src/admin/fixtures/ProjectFileManagerFixture.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
51
src/admin/fixtures/ProjectsFixture.tsx
Normal file
51
src/admin/fixtures/ProjectsFixture.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
80
src/admin/fixtures/ReceivedInvoicesFixture.tsx
Normal file
80
src/admin/fixtures/ReceivedInvoicesFixture.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
70
src/admin/fixtures/SettingsFixture.tsx
Normal file
70
src/admin/fixtures/SettingsFixture.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
44
src/admin/fixtures/TripsAdminFixture.tsx
Normal file
44
src/admin/fixtures/TripsAdminFixture.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
132
src/admin/fixtures/TripsFixture.tsx
Normal file
132
src/admin/fixtures/TripsFixture.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
40
src/admin/fixtures/TripsHistoryFixture.tsx
Normal file
40
src/admin/fixtures/TripsHistoryFixture.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
66
src/admin/fixtures/UsersFixture.tsx
Normal file
66
src/admin/fixtures/UsersFixture.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
48
src/admin/fixtures/VehiclesFixture.tsx
Normal file
48
src/admin/fixtures/VehiclesFixture.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
@@ -23,8 +23,9 @@ export default function useApiCall() {
|
||||
abortRef.current = controller;
|
||||
|
||||
try {
|
||||
const { signal: _, ...restOptions } = options;
|
||||
const response = await apiFetch(url, {
|
||||
...options,
|
||||
...restOptions,
|
||||
signal: controller.signal,
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
@@ -224,11 +224,20 @@ function computeUserTotals(
|
||||
// Print helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function escapeHtml(str: string): string {
|
||||
return str
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
function renderFundStatus(userData: Record<string, any>): string {
|
||||
if (userData.overtime > 0)
|
||||
return `<span class="leave-badge badge-overtime">+${userData.overtime}h přesčas</span>`;
|
||||
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">−${userData.missing}h</span>`;
|
||||
return `<span style="color:#dc2626">−${escapeHtml(String(userData.missing))}h</span>`;
|
||||
return '<span style="color:#16a34a">splněno</span>';
|
||||
}
|
||||
|
||||
@@ -255,11 +264,11 @@ function buildProjectLogsHtml(record: Record<string, any>): string {
|
||||
h = 0;
|
||||
m = 0;
|
||||
}
|
||||
return `<div>${log.project_name || `#${log.project_id}`} (${h}:${String(m).padStart(2, "0")}h)</div>`;
|
||||
return `<div>${escapeHtml(log.project_name || `#${log.project_id}`)} (${h}:${String(m).padStart(2, "0")}h)</div>`;
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
return record.project_name || "—";
|
||||
return escapeHtml(record.project_name || "—");
|
||||
}
|
||||
|
||||
function buildLeaveSummaryHtml(
|
||||
@@ -268,15 +277,15 @@ function buildLeaveSummaryHtml(
|
||||
printData: Record<string, any>,
|
||||
): string {
|
||||
const bal = printData.leave_balances[userId];
|
||||
let parts = `<strong>Dovolená ${printData.year}:</strong> Zbývá ${bal.vacation_remaining.toFixed(1)}h z ${bal.vacation_total}h`;
|
||||
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: ${userData.vacation_hours}h</span>`;
|
||||
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: ${userData.sick_hours}h</span>`;
|
||||
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: ${userData.holiday_hours}h</span>`;
|
||||
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: +${userData.overtime}h</span>`;
|
||||
parts += ` | <span class="leave-badge badge-overtime">Přesčas: +${escapeHtml(String(userData.overtime))}h</span>`;
|
||||
return `<div class="leave-summary">${parts}</div>`;
|
||||
}
|
||||
|
||||
@@ -299,17 +308,17 @@ function buildUserSectionHtml(
|
||||
const breakCell =
|
||||
isLeave || !record.break_start || !record.break_end
|
||||
? "—"
|
||||
: `${formatTimeOrDatetimePrint(record.break_start, record.shift_date)} - ${formatTimeOrDatetimePrint(record.break_end, record.shift_date)}`;
|
||||
: `${escapeHtml(formatTimeOrDatetimePrint(record.break_start, record.shift_date))} - ${escapeHtml(formatTimeOrDatetimePrint(record.break_end, record.shift_date))}`;
|
||||
|
||||
return `<tr>
|
||||
<td>${formatDate(record.shift_date)}</td>
|
||||
<td><span class="leave-badge ${getLeaveTypeBadgeClass(leaveType)}">${getLeaveTypeName(leaveType)}</span></td>
|
||||
<td class="text-center">${isLeave ? "—" : formatTimeOrDatetimePrint(record.arrival_time, record.shift_date)}</td>
|
||||
<td>${escapeHtml(formatDate(record.shift_date))}</td>
|
||||
<td><span class="leave-badge ${escapeHtml(getLeaveTypeBadgeClass(leaveType))}">${escapeHtml(getLeaveTypeName(leaveType))}</span></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">${isLeave ? "—" : 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 style="font-size:8px">${buildProjectLogsHtml(record)}</td>
|
||||
<td>${record.notes || ""}</td>
|
||||
<td>${escapeHtml(record.notes || "")}</td>
|
||||
</tr>`;
|
||||
})
|
||||
.join("");
|
||||
@@ -318,15 +327,15 @@ function buildUserSectionHtml(
|
||||
userData.fund !== null
|
||||
? `<tr>
|
||||
<td colspan="6" class="text-right">Fond měsíce:</td>
|
||||
<td class="text-center">${userData.covered}h / ${userData.fund}h</td>
|
||||
<td class="text-center">${escapeHtml(String(userData.covered))}h / ${escapeHtml(String(userData.fund))}h</td>
|
||||
<td colspan="2">${renderFundStatus(userData)}</td>
|
||||
</tr>`
|
||||
: "";
|
||||
|
||||
return `<div class="user-section">
|
||||
<div class="user-header">
|
||||
<h3>${userData.name}</h3>
|
||||
<span class="total">Odpracováno: ${formatMinutes(userData.minutes)} h</span>
|
||||
<h3>${escapeHtml(userData.name)}</h3>
|
||||
<span class="total">Odpracováno: ${escapeHtml(formatMinutes(userData.minutes))} h</span>
|
||||
</div>
|
||||
${leaveHtml}
|
||||
<table>
|
||||
@@ -344,7 +353,7 @@ function buildUserSectionHtml(
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="6" class="text-right">Odpracováno:</td>
|
||||
<td class="text-center">${formatMinutes(userData.minutes)} h</td>
|
||||
<td class="text-center">${escapeHtml(formatMinutes(userData.minutes))} h</td>
|
||||
<td colspan="2"></td>
|
||||
</tr>
|
||||
${fundRow}
|
||||
@@ -365,7 +374,7 @@ function buildPrintHtml(
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Docházka - ${pData.month_name}</title>
|
||||
<title>Docházka - ${escapeHtml(pData.month_name)}</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
@@ -428,11 +437,11 @@ function buildPrintHtml(
|
||||
<img src="/api/admin/company-settings/logo?variant=light" alt="" class="print-logo" />
|
||||
<div class="print-header-text">
|
||||
<h1>EVIDENCE DOCHÁZKY</h1>
|
||||
<div class="company">${companyName}</div>
|
||||
<div class="company">${escapeHtml(companyName)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="print-header-right">
|
||||
<div class="period">${pData.month_name}</div>
|
||||
<div class="period">${escapeHtml(pData.month_name)}</div>
|
||||
${filterNote}
|
||||
<div class="generated">Vygenerováno: ${new Date().toLocaleString("cs-CZ")}</div>
|
||||
</div>
|
||||
@@ -1037,7 +1046,7 @@ export default function useAttendanceAdmin({ alert }: AlertContext) {
|
||||
? '<p style="text-align:center;padding:20px">Za vybrané období nejsou žádné záznamy.</p>'
|
||||
: "";
|
||||
const filterNote = pData.selected_user_name
|
||||
? `<div class="filters">Zaměstnanec: ${pData.selected_user_name}</div>`
|
||||
? `<div class="filters">Zaměstnanec: ${escapeHtml(pData.selected_user_name)}</div>`
|
||||
: "";
|
||||
const bodyContent = buildPrintHtml(
|
||||
pData,
|
||||
@@ -1051,7 +1060,9 @@ export default function useAttendanceAdmin({ alert }: AlertContext) {
|
||||
printWindow.document.open();
|
||||
printWindow.document.write(bodyContent);
|
||||
printWindow.document.close();
|
||||
printWindow.onload = () => printWindow.print();
|
||||
printWindow.addEventListener("load", () => printWindow.print(), {
|
||||
once: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -43,8 +43,14 @@ export default function useListData<T = unknown>(
|
||||
const [initialLoad, setInitialLoad] = useState(true);
|
||||
const [pagination, setPagination] = useState<PaginationData | null>(null);
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
const mountedRef = useRef(true);
|
||||
const debouncedSearch = useDebounce(search, 300);
|
||||
|
||||
const extraParamsKey = Object.entries(extraParams)
|
||||
.sort((a, b) => a[0].localeCompare(b[0]))
|
||||
.map(([k, v]) => `${k}=${v}`)
|
||||
.join("&");
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
if (abortRef.current) abortRef.current.abort();
|
||||
const controller = new AbortController();
|
||||
@@ -66,7 +72,10 @@ export default function useListData<T = unknown>(
|
||||
? `${endpoint}?${params}`
|
||||
: `${API_BASE}/${endpoint}?${params}`;
|
||||
const response = await apiFetch(url, { signal: controller.signal });
|
||||
if (response.status === 401) return;
|
||||
if (response.status === 401) {
|
||||
window.location.href = "/login";
|
||||
return;
|
||||
}
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
const data = dataKey
|
||||
@@ -92,8 +101,10 @@ export default function useListData<T = unknown>(
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
if (err instanceof Error && err.name === "AbortError") return;
|
||||
if (!mountedRef.current) return;
|
||||
alert.error(errorMsg);
|
||||
} finally {
|
||||
if (!mountedRef.current) return;
|
||||
setLoading(false);
|
||||
setInitialLoad(false);
|
||||
}
|
||||
@@ -105,12 +116,14 @@ export default function useListData<T = unknown>(
|
||||
page,
|
||||
perPage,
|
||||
dataKey,
|
||||
JSON.stringify(extraParams),
|
||||
]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
extraParamsKey,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
mountedRef.current = true;
|
||||
fetchData();
|
||||
return () => {
|
||||
mountedRef.current = false;
|
||||
if (abortRef.current) abortRef.current.abort();
|
||||
};
|
||||
}, [fetchData]);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { useEffect } from "react";
|
||||
|
||||
let activeLocks = 0;
|
||||
|
||||
export default function useModalLock(isOpen: boolean): void {
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
document.body.style.overflow = "hidden";
|
||||
} else {
|
||||
document.body.style.overflow = "";
|
||||
if (activeLocks === 0) document.body.style.overflow = "hidden";
|
||||
activeLocks++;
|
||||
return () => {
|
||||
activeLocks = Math.max(0, activeLocks - 1);
|
||||
if (activeLocks === 0) document.body.style.overflow = "";
|
||||
};
|
||||
}
|
||||
return () => {
|
||||
document.body.style.overflow = "";
|
||||
};
|
||||
}, [isOpen]);
|
||||
}
|
||||
|
||||
44
src/admin/hooks/usePaginatedQuery.ts
Normal file
44
src/admin/hooks/usePaginatedQuery.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
useQuery,
|
||||
keepPreviousData,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
import type { UseQueryOptions } from "@tanstack/react-query";
|
||||
|
||||
interface PaginatedResult<T> {
|
||||
data: T[];
|
||||
pagination: {
|
||||
total: number;
|
||||
page: number;
|
||||
per_page: number;
|
||||
total_pages: number;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper around useQuery for paginated list endpoints.
|
||||
* Accepts the return value of queryOptions() from lib/queries/*
|
||||
* and extracts items + pagination from the response.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function usePaginatedQuery<T>(options: any) {
|
||||
const query = useQuery({
|
||||
...options,
|
||||
placeholderData: keepPreviousData,
|
||||
} as UseQueryOptions<PaginatedResult<T>>);
|
||||
|
||||
const data = query.data as PaginatedResult<T> | undefined;
|
||||
|
||||
return {
|
||||
items: (data?.data ?? []) as T[],
|
||||
pagination: data?.pagination ?? null,
|
||||
isPending: query.isPending,
|
||||
isFetching: query.isFetching,
|
||||
isPlaceholderData: query.isPlaceholderData,
|
||||
isError: query.isError,
|
||||
error: query.error,
|
||||
refetch: query.refetch,
|
||||
};
|
||||
}
|
||||
|
||||
export { useQueryClient, useQuery, keepPreviousData };
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useCallback, useRef } from "react";
|
||||
import { useState, useCallback } from "react";
|
||||
|
||||
interface SortState {
|
||||
sort: string;
|
||||
@@ -13,10 +13,10 @@ export default function useTableSort(
|
||||
sort: defaultSort,
|
||||
order: defaultOrder,
|
||||
});
|
||||
const userClicked = useRef(false);
|
||||
const [userClicked, setUserClicked] = useState(false);
|
||||
|
||||
const handleSort = useCallback((column: string) => {
|
||||
userClicked.current = true;
|
||||
setUserClicked(true);
|
||||
setState((prev) => {
|
||||
if (prev.sort === column) {
|
||||
return { sort: column, order: prev.order === "asc" ? "desc" : "asc" };
|
||||
@@ -25,7 +25,7 @@ export default function useTableSort(
|
||||
});
|
||||
}, []);
|
||||
|
||||
const activeSort = userClicked.current ? state.sort : null;
|
||||
const activeSort = userClicked ? state.sort : null;
|
||||
|
||||
return { sort: state.sort, order: state.order, handleSort, activeSort };
|
||||
}
|
||||
|
||||
83
src/admin/lib/apiAdapter.ts
Normal file
83
src/admin/lib/apiAdapter.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import apiFetch from "../utils/api";
|
||||
|
||||
/**
|
||||
* Thin adapter that converts apiFetch responses into the shape TanStack Query expects.
|
||||
* - Checks response.ok and result.success
|
||||
* - Throws on errors so TanStack Query can handle retry/error states
|
||||
* - Returns result.data directly (unwrapped from the API envelope)
|
||||
*/
|
||||
export async function jsonQuery<T>(
|
||||
url: string,
|
||||
options?: RequestInit,
|
||||
): Promise<T> {
|
||||
const response = await apiFetch(url, options);
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
let result: { success: boolean; data?: unknown; error?: string };
|
||||
try {
|
||||
result = (await response.json()) as typeof result;
|
||||
} catch {
|
||||
throw new Error("Invalid JSON response");
|
||||
}
|
||||
|
||||
if (!response.ok || !result.success) {
|
||||
throw new Error(result.error || `Request failed (${response.status})`);
|
||||
}
|
||||
|
||||
return result.data as T;
|
||||
}
|
||||
|
||||
export interface PaginationMeta {
|
||||
total: number;
|
||||
page: number;
|
||||
per_page: number;
|
||||
total_pages: number;
|
||||
}
|
||||
|
||||
export interface PaginatedResult<T> {
|
||||
data: T[];
|
||||
pagination: PaginationMeta;
|
||||
}
|
||||
|
||||
export async function paginatedJsonQuery<T>(
|
||||
url: string,
|
||||
options?: RequestInit,
|
||||
): Promise<PaginatedResult<T>> {
|
||||
const response = await apiFetch(url, options);
|
||||
|
||||
if (response.status === 401) {
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
let result: {
|
||||
success: boolean;
|
||||
data?: unknown;
|
||||
error?: string;
|
||||
pagination?: PaginationMeta;
|
||||
};
|
||||
try {
|
||||
result = (await response.json()) as typeof result;
|
||||
} catch {
|
||||
throw new Error("Invalid JSON response");
|
||||
}
|
||||
|
||||
if (!response.ok || !result.success) {
|
||||
throw new Error(result.error || `Request failed (${response.status})`);
|
||||
}
|
||||
|
||||
const items = Array.isArray(result.data)
|
||||
? result.data
|
||||
: ((result.data as { items?: T[] })?.items ?? []);
|
||||
const pagination = result.pagination ??
|
||||
(result.data as { pagination?: PaginationMeta })?.pagination ?? {
|
||||
total: items.length,
|
||||
page: 1,
|
||||
per_page: items.length,
|
||||
total_pages: 1,
|
||||
};
|
||||
|
||||
return { data: items as T[], pagination };
|
||||
}
|
||||
99
src/admin/lib/queries/attendance.ts
Normal file
99
src/admin/lib/queries/attendance.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import apiFetch from "../../utils/api";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
interface LocationRaw {
|
||||
users?: { first_name: string; last_name: string };
|
||||
user_name?: string;
|
||||
shift_date: string;
|
||||
arrival_time?: string | null;
|
||||
departure_time?: string | null;
|
||||
arrival_lat?: string | number | null;
|
||||
arrival_lng?: string | number | null;
|
||||
arrival_accuracy?: number | null;
|
||||
arrival_address?: string | null;
|
||||
departure_lat?: string | number | null;
|
||||
departure_lng?: string | number | null;
|
||||
departure_accuracy?: number | null;
|
||||
departure_address?: string | null;
|
||||
}
|
||||
|
||||
export interface LocationRecord {
|
||||
user_name: string;
|
||||
shift_date: string;
|
||||
arrival_time?: string | null;
|
||||
departure_time?: string | null;
|
||||
arrival_lat?: string | number | null;
|
||||
arrival_lng?: string | number | null;
|
||||
arrival_accuracy?: number | null;
|
||||
arrival_address?: string | null;
|
||||
departure_lat?: string | number | null;
|
||||
departure_lng?: string | number | null;
|
||||
departure_accuracy?: number | null;
|
||||
departure_address?: string | null;
|
||||
}
|
||||
|
||||
export const attendanceHistoryOptions = (filters: {
|
||||
month?: string;
|
||||
userId?: number;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "history", filters],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
params.set("limit", "1000");
|
||||
if (filters.month) params.set("month", filters.month);
|
||||
if (filters.userId) params.set("user_id", String(filters.userId));
|
||||
return jsonQuery<Record<string, unknown>[]>(
|
||||
`/api/admin/attendance?${params.toString()}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const attendanceBalancesOptions = (year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "balances", year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
`/api/admin/attendance?action=balances&year=${year}`,
|
||||
),
|
||||
});
|
||||
|
||||
export const attendanceWorkFundOptions = (year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "workfund", year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
`/api/admin/attendance?action=workfund&year=${year}`,
|
||||
),
|
||||
});
|
||||
|
||||
export const attendanceProjectReportOptions = (year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "project-report", year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
`/api/admin/attendance?action=project_report&year=${year}`,
|
||||
),
|
||||
});
|
||||
|
||||
export const attendanceLocationOptions = (id: string | undefined) =>
|
||||
queryOptions({
|
||||
queryKey: ["attendance", "location", id],
|
||||
queryFn: async (): Promise<LocationRecord> => {
|
||||
const response = await apiFetch(
|
||||
`/api/admin/attendance?action=location&id=${id}`,
|
||||
);
|
||||
const result = await response.json();
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || "Záznam nebyl nalezen");
|
||||
}
|
||||
const raw = (result.data.record || result.data) as LocationRaw;
|
||||
const userName = raw.users
|
||||
? `${raw.users.first_name} ${raw.users.last_name}`.trim()
|
||||
: raw.user_name || "";
|
||||
const { users: _users, ...rest } = raw;
|
||||
return { ...rest, user_name: userName };
|
||||
},
|
||||
enabled: !!id,
|
||||
});
|
||||
28
src/admin/lib/queries/auditLog.ts
Normal file
28
src/admin/lib/queries/auditLog.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export const auditLogOptions = (filters: {
|
||||
search?: string;
|
||||
action?: string;
|
||||
entityType?: string;
|
||||
dateFrom?: string;
|
||||
dateTo?: string;
|
||||
page?: number;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: ["audit-log", filters],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.search) params.set("search", filters.search);
|
||||
if (filters.action) params.set("action", filters.action);
|
||||
if (filters.entityType) params.set("entity_type", filters.entityType);
|
||||
if (filters.dateFrom) params.set("date_from", filters.dateFrom);
|
||||
if (filters.dateTo) params.set("date_to", filters.dateTo);
|
||||
if (filters.page) params.set("page", String(filters.page));
|
||||
const qs = params.toString();
|
||||
return jsonQuery<{
|
||||
data: Record<string, unknown>[];
|
||||
pagination: Record<string, unknown>;
|
||||
}>(`/api/admin/audit-log${qs ? `?${qs}` : ""}`);
|
||||
},
|
||||
});
|
||||
18
src/admin/lib/queries/common.ts
Normal file
18
src/admin/lib/queries/common.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export const bankAccountsOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["bank-accounts"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>[]>("/api/admin/bank-accounts"),
|
||||
staleTime: 2 * 60_000,
|
||||
});
|
||||
|
||||
export const supplierListOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["suppliers"],
|
||||
queryFn: () =>
|
||||
jsonQuery<string[]>("/api/admin/received-invoices/suppliers"),
|
||||
staleTime: 2 * 60_000,
|
||||
});
|
||||
42
src/admin/lib/queries/dashboard.ts
Normal file
42
src/admin/lib/queries/dashboard.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import apiFetch from "../../utils/api";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export const dashboardOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["dashboard"],
|
||||
queryFn: () => jsonQuery<Record<string, unknown>>("/api/admin/dashboard"),
|
||||
staleTime: 60_000,
|
||||
});
|
||||
|
||||
export const require2FAOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["settings", "2fa"],
|
||||
queryFn: () =>
|
||||
jsonQuery<{ require_2fa: boolean }>("/api/admin/totp/required"),
|
||||
});
|
||||
|
||||
export interface Session {
|
||||
id: number | string;
|
||||
is_current: boolean;
|
||||
device_info?: {
|
||||
icon?: string;
|
||||
browser?: string;
|
||||
os?: string;
|
||||
};
|
||||
ip_address: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const sessionsOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["sessions"],
|
||||
queryFn: async (): Promise<Session[]> => {
|
||||
const response = await apiFetch("/api/admin/sessions");
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
return Array.isArray(data.data) ? data.data : data.data?.sessions || [];
|
||||
}
|
||||
throw new Error(data.error || "Nepodařilo se načíst relace");
|
||||
},
|
||||
});
|
||||
126
src/admin/lib/queries/invoices.ts
Normal file
126
src/admin/lib/queries/invoices.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery, paginatedJsonQuery } from "../apiAdapter";
|
||||
|
||||
export interface CurrencyAmount {
|
||||
amount: number;
|
||||
currency: string;
|
||||
}
|
||||
|
||||
export interface Invoice {
|
||||
id: number;
|
||||
invoice_number: string;
|
||||
customer_name: string | null;
|
||||
status: string;
|
||||
issue_date: string;
|
||||
due_date: string;
|
||||
total: number;
|
||||
currency: string;
|
||||
}
|
||||
|
||||
export interface InvoiceStats {
|
||||
paid_month: CurrencyAmount[];
|
||||
paid_month_czk: number;
|
||||
paid_month_count: number;
|
||||
awaiting: CurrencyAmount[];
|
||||
awaiting_czk: number;
|
||||
awaiting_count: number;
|
||||
overdue: CurrencyAmount[];
|
||||
overdue_czk: number;
|
||||
overdue_count: number;
|
||||
vat_month: CurrencyAmount[];
|
||||
vat_month_czk: number;
|
||||
}
|
||||
|
||||
export const invoiceListOptions = (filters: {
|
||||
search?: string;
|
||||
sort?: string;
|
||||
order?: string;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
month?: number;
|
||||
year?: number;
|
||||
status?: string;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: ["invoices", "list", filters],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.search) params.set("search", filters.search);
|
||||
if (filters.sort) params.set("sort", filters.sort);
|
||||
if (filters.order) params.set("order", filters.order);
|
||||
if (filters.page) params.set("page", String(filters.page));
|
||||
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||
if (filters.month) params.set("month", String(filters.month));
|
||||
if (filters.year) params.set("year", String(filters.year));
|
||||
if (filters.status) params.set("status", filters.status);
|
||||
const qs = params.toString();
|
||||
return paginatedJsonQuery<Invoice>(
|
||||
`/api/admin/invoices${qs ? `?${qs}` : ""}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const receivedInvoiceListOptions = (filters: {
|
||||
month?: number;
|
||||
year?: number;
|
||||
search?: string;
|
||||
sort?: string;
|
||||
order?: string;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: [
|
||||
"invoices",
|
||||
"received",
|
||||
{
|
||||
month: filters.month,
|
||||
year: filters.year,
|
||||
search: filters.search,
|
||||
sort: filters.sort,
|
||||
order: filters.order,
|
||||
page: filters.page,
|
||||
perPage: filters.perPage,
|
||||
},
|
||||
],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.month) params.set("month", String(filters.month));
|
||||
if (filters.year) params.set("year", String(filters.year));
|
||||
if (filters.search) params.set("search", filters.search);
|
||||
if (filters.sort) params.set("sort", filters.sort);
|
||||
if (filters.order) params.set("order", filters.order);
|
||||
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(
|
||||
`/api/admin/received-invoices${qs ? `?${qs}` : ""}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const invoiceStatsOptions = (month: number, year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["invoices", "stats", month, year],
|
||||
queryFn: () =>
|
||||
jsonQuery<InvoiceStats>(
|
||||
`/api/admin/invoices/stats?month=${month}&year=${year}`,
|
||||
),
|
||||
});
|
||||
|
||||
export const receivedInvoiceStatsOptions = (month: number, year: number) =>
|
||||
queryOptions({
|
||||
queryKey: ["invoices", "received", "stats", month, year],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
`/api/admin/received-invoices/stats?month=${month}&year=${year}`,
|
||||
),
|
||||
});
|
||||
|
||||
export const invoiceDetailOptions = (id: string | undefined) =>
|
||||
queryOptions({
|
||||
queryKey: ["invoices", id],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(`/api/admin/invoices/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
29
src/admin/lib/queries/leave.ts
Normal file
29
src/admin/lib/queries/leave.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export const leaveRequestsOptions = (mine = true) =>
|
||||
queryOptions({
|
||||
queryKey: ["leave-requests", mine ? "mine" : "all"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
`/api/admin/leave-requests${mine ? "?mine=1" : ""}`,
|
||||
),
|
||||
});
|
||||
|
||||
export const leavePendingOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["leave", "pending"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>[]>(
|
||||
"/api/admin/leave-requests?status=pending",
|
||||
),
|
||||
});
|
||||
|
||||
export const leaveProcessedOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["leave", "processed"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>[]>(
|
||||
"/api/admin/leave-requests?status=approved,rejected",
|
||||
),
|
||||
});
|
||||
58
src/admin/lib/queries/offers.ts
Normal file
58
src/admin/lib/queries/offers.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery, paginatedJsonQuery } from "../apiAdapter";
|
||||
|
||||
export const offerCustomersOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["offer-customers"],
|
||||
queryFn: () => jsonQuery<Record<string, unknown>>("/api/admin/customers"),
|
||||
staleTime: 2 * 60_000,
|
||||
});
|
||||
|
||||
export const offerTemplatesOptions = (action?: string) =>
|
||||
queryOptions({
|
||||
queryKey: ["offer-templates", action ?? "all"],
|
||||
queryFn: () => {
|
||||
const url = action
|
||||
? `/api/admin/offers-templates?action=${action}`
|
||||
: "/api/admin/offers-templates";
|
||||
return jsonQuery<Record<string, unknown>[]>(url);
|
||||
},
|
||||
});
|
||||
|
||||
export const offerListOptions = (filters: {
|
||||
search?: string;
|
||||
sort?: string;
|
||||
order?: string;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: ["offers", "list", filters],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.search) params.set("search", filters.search);
|
||||
if (filters.sort) params.set("sort", filters.sort);
|
||||
if (filters.order) params.set("order", filters.order);
|
||||
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(`/api/admin/offers${qs ? `?${qs}` : ""}`);
|
||||
},
|
||||
});
|
||||
|
||||
export const offerDetailOptions = (id: string | undefined) =>
|
||||
queryOptions({
|
||||
queryKey: ["offers", id],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(`/api/admin/offers/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
|
||||
export const offerNextNumberOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["offers", "next-number"],
|
||||
queryFn: () =>
|
||||
jsonQuery<{ next_number?: string; number?: string }>(
|
||||
"/api/admin/offers/next-number",
|
||||
),
|
||||
});
|
||||
31
src/admin/lib/queries/orders.ts
Normal file
31
src/admin/lib/queries/orders.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery, paginatedJsonQuery } from "../apiAdapter";
|
||||
|
||||
export const orderListOptions = (filters: {
|
||||
search?: string;
|
||||
sort?: string;
|
||||
order?: string;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: ["orders", "list", filters],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.search) params.set("search", filters.search);
|
||||
if (filters.sort) params.set("sort", filters.sort);
|
||||
if (filters.order) params.set("order", filters.order);
|
||||
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(`/api/admin/orders${qs ? `?${qs}` : ""}`);
|
||||
},
|
||||
});
|
||||
|
||||
export const orderDetailOptions = (id: string | undefined) =>
|
||||
queryOptions({
|
||||
queryKey: ["orders", id],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(`/api/admin/orders/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
78
src/admin/lib/queries/projects.ts
Normal file
78
src/admin/lib/queries/projects.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery, paginatedJsonQuery } from "../apiAdapter";
|
||||
import apiFetch from "../../utils/api";
|
||||
|
||||
export const projectListOptions = (filters: {
|
||||
search?: string;
|
||||
sort?: string;
|
||||
order?: string;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: ["projects", "list", filters],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.search) params.set("search", filters.search);
|
||||
if (filters.sort) params.set("sort", filters.sort);
|
||||
if (filters.order) params.set("order", filters.order);
|
||||
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(`/api/admin/projects${qs ? `?${qs}` : ""}`);
|
||||
},
|
||||
});
|
||||
|
||||
export const projectDetailOptions = (id: string | undefined) =>
|
||||
queryOptions({
|
||||
queryKey: ["projects", id],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(`/api/admin/projects/${id}`),
|
||||
enabled: !!id,
|
||||
});
|
||||
|
||||
export interface ProjectFilesData {
|
||||
items: Array<{
|
||||
name: string;
|
||||
type: "file" | "folder";
|
||||
size?: number;
|
||||
size_formatted?: string;
|
||||
modified?: string;
|
||||
extension?: string;
|
||||
item_count?: number;
|
||||
is_symlink?: boolean;
|
||||
link_target?: string;
|
||||
}>;
|
||||
breadcrumb: string[];
|
||||
path: string;
|
||||
full_path: string;
|
||||
}
|
||||
|
||||
export const projectFilesOptions = (projectId: number, path: string) =>
|
||||
queryOptions({
|
||||
queryKey: ["projects", String(projectId), "files", path],
|
||||
queryFn: async (): Promise<ProjectFilesData> => {
|
||||
const params = new URLSearchParams({ project_id: String(projectId) });
|
||||
if (path) params.set("path", path);
|
||||
let res: Response;
|
||||
try {
|
||||
res = await apiFetch(`/api/admin/project-files?${params}`);
|
||||
} catch {
|
||||
throw new Error("Chyba připojení");
|
||||
}
|
||||
if (res.status === 401) throw new Error("Unauthorized");
|
||||
if (res.status === 404) {
|
||||
return { items: [], breadcrumb: [""], path: "", full_path: "" };
|
||||
}
|
||||
const data = await res.json();
|
||||
if (!res.ok || !data.success) {
|
||||
throw new Error(data.error || `Request failed (${res.status})`);
|
||||
}
|
||||
return {
|
||||
items: data.data.items || [],
|
||||
breadcrumb: data.data.breadcrumb || [""],
|
||||
path: data.data.path || "",
|
||||
full_path: data.data.full_path || "",
|
||||
};
|
||||
},
|
||||
});
|
||||
29
src/admin/lib/queries/settings.ts
Normal file
29
src/admin/lib/queries/settings.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export const companySettingsOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["company-settings"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>("/api/admin/company-settings"),
|
||||
staleTime: 5 * 60_000,
|
||||
});
|
||||
|
||||
export const systemInfoOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["settings", "system-info"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>>(
|
||||
"/api/admin/company-settings/system-info",
|
||||
),
|
||||
});
|
||||
|
||||
/** @deprecated Use systemInfoOptions instead — this query fetches system-info, not system settings. */
|
||||
export const systemSettingsOptions = systemInfoOptions;
|
||||
|
||||
export const require2FAOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["settings", "2fa"],
|
||||
queryFn: () =>
|
||||
jsonQuery<{ require_2fa: boolean }>("/api/admin/totp/required"),
|
||||
});
|
||||
82
src/admin/lib/queries/trips.ts
Normal file
82
src/admin/lib/queries/trips.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { queryOptions } from "@tanstack/react-query";
|
||||
import { jsonQuery } from "../apiAdapter";
|
||||
|
||||
export const tripListOptions = (filters: {
|
||||
month?: number;
|
||||
year?: number;
|
||||
vehicleId?: number;
|
||||
userId?: number;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: [
|
||||
"trips",
|
||||
"list",
|
||||
{
|
||||
month: filters.month,
|
||||
year: filters.year,
|
||||
vehicleId: filters.vehicleId,
|
||||
userId: filters.userId,
|
||||
page: filters.page,
|
||||
perPage: filters.perPage,
|
||||
},
|
||||
],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.month) params.set("month", String(filters.month));
|
||||
if (filters.year) params.set("year", String(filters.year));
|
||||
if (filters.vehicleId)
|
||||
params.set("vehicle_id", String(filters.vehicleId));
|
||||
if (filters.userId) params.set("user_id", String(filters.userId));
|
||||
if (filters.page) params.set("page", String(filters.page));
|
||||
if (filters.perPage) params.set("per_page", String(filters.perPage));
|
||||
const qs = params.toString();
|
||||
return jsonQuery<Record<string, unknown>[]>(
|
||||
`/api/admin/trips${qs ? `?${qs}` : ""}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const tripVehiclesOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["trips", "vehicles"],
|
||||
queryFn: () => jsonQuery<Record<string, unknown>[]>("/api/admin/vehicles"),
|
||||
staleTime: 2 * 60_000,
|
||||
});
|
||||
|
||||
export const tripUsersOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["trips", "users"],
|
||||
queryFn: () =>
|
||||
jsonQuery<Record<string, unknown>[]>("/api/admin/trips/users"),
|
||||
staleTime: 2 * 60_000,
|
||||
});
|
||||
|
||||
export const tripHistoryOptions = (filters: {
|
||||
month?: string;
|
||||
vehicleId?: number;
|
||||
userId?: number;
|
||||
}) =>
|
||||
queryOptions({
|
||||
queryKey: [
|
||||
"trips",
|
||||
"history",
|
||||
{
|
||||
month: filters.month,
|
||||
vehicleId: filters.vehicleId,
|
||||
userId: filters.userId,
|
||||
},
|
||||
],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
if (filters.month) params.set("month", filters.month);
|
||||
if (filters.vehicleId)
|
||||
params.set("vehicle_id", String(filters.vehicleId));
|
||||
if (filters.userId) params.set("user_id", String(filters.userId));
|
||||
const qs = params.toString();
|
||||
return jsonQuery<Record<string, unknown>[]>(
|
||||
`/api/admin/trips${qs ? `?${qs}` : ""}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user