Commit 00f555fb authored by Mahmoud Aglan's avatar Mahmoud Aglan

test

parent a4373d67
......@@ -36,18 +36,25 @@ class PoolGridApiController extends Controller
{
$body = $request->jsonBody();
$date = trim((string) ($body['date'] ?? ''));
$startTime = trim((string) ($body['start_time'] ?? ''));
$endTime = trim((string) ($body['end_time'] ?? ''));
$action = trim((string) ($body['action'] ?? ''));
$cells = $body['cells'] ?? [];
$groupId = !empty($body['group_id']) ? (int) $body['group_id'] : null;
$notes = trim((string) ($body['notes'] ?? ''));
if (!$date || !$startTime || !$endTime || !$action || empty($cells)) {
// Support multi-slot (slots array) or legacy single start_time/end_time
if (!empty($body['slots']) && is_array($body['slots'])) {
$slots = $body['slots'];
} else {
$startTime = trim((string) ($body['start_time'] ?? ''));
$endTime = trim((string) ($body['end_time'] ?? ''));
$slots = [['start_time' => $startTime, 'end_time' => $endTime]];
}
if (!$date || !$action || empty($cells) || empty($slots)) {
return $this->json(['error' => 'بيانات ناقصة'], 400);
}
$result = PoolGridService::bulkAssign((int) $id, $date, $startTime, $endTime, $action, $cells, $groupId, $notes ?: null);
$result = PoolGridService::bulkAssign((int) $id, $date, $slots, $action, $cells, $groupId, $notes ?: null);
return $this->json($result, $result['success'] ? 200 : 422);
}
......@@ -56,14 +63,21 @@ class PoolGridApiController extends Controller
{
$body = $request->jsonBody();
$date = trim((string) ($body['date'] ?? ''));
$startTime = trim((string) ($body['start_time'] ?? ''));
$cells = $body['cells'] ?? [];
if (!$date || !$startTime || empty($cells)) {
// Support multi-slot or legacy single start_time
if (!empty($body['slots']) && is_array($body['slots'])) {
$slots = $body['slots'];
} else {
$startTime = trim((string) ($body['start_time'] ?? ''));
$slots = [['start_time' => $startTime]];
}
if (!$date || empty($cells) || empty($slots)) {
return $this->json(['error' => 'بيانات ناقصة'], 400);
}
$result = PoolGridService::bulkClear((int) $id, $date, $startTime, $cells);
$result = PoolGridService::bulkClear((int) $id, $date, $slots, $cells);
return $this->json($result);
}
......@@ -88,14 +102,21 @@ class PoolGridApiController extends Controller
$body = $request->jsonBody();
$date = trim((string) ($body['date'] ?? ''));
$fromTime = trim((string) ($body['from_time'] ?? ''));
$toTime = trim((string) ($body['to_time'] ?? ''));
$toEndTime = trim((string) ($body['to_end_time'] ?? ''));
if (!$date || !$fromTime || !$toTime || !$toEndTime) {
// Support multi-target (to_slots array) or legacy single to_time/to_end_time
if (!empty($body['to_slots']) && is_array($body['to_slots'])) {
$toSlots = $body['to_slots'];
} else {
$toTime = trim((string) ($body['to_time'] ?? ''));
$toEndTime = trim((string) ($body['to_end_time'] ?? ''));
$toSlots = [['start' => $toTime, 'end' => $toEndTime]];
}
if (!$date || !$fromTime || empty($toSlots)) {
return $this->json(['error' => 'بيانات ناقصة'], 400);
}
$result = PoolGridService::copySlot((int) $id, $date, $fromTime, $toTime, $toEndTime);
$result = PoolGridService::copySlot((int) $id, $date, $fromTime, $toSlots);
return $this->json($result, $result['success'] ? 200 : 422);
}
......
<?php
declare(strict_types=1);
namespace App\Modules\SportsActivity\Controllers\Api;
use App\Core\Controller;
use App\Core\Request;
use App\Core\Response;
use App\Modules\SportsActivity\Services\PoolGridTemplateService;
class PoolGridTemplateApiController extends Controller
{
public function list(Request $request, string $id): Response
{
$templates = PoolGridTemplateService::getTemplates((int) $id);
return $this->json(['templates' => $templates]);
}
public function store(Request $request, string $id): Response
{
$body = $request->jsonBody();
$name = trim((string) ($body['name'] ?? ''));
$description = trim((string) ($body['description'] ?? ''));
$color = trim((string) ($body['color'] ?? '#3B82F6'));
$entries = $body['entries'] ?? [];
if (!$name) {
return $this->json(['error' => 'اسم القالب مطلوب'], 400);
}
$result = PoolGridTemplateService::createTemplate((int) $id, $name, $description ?: null, $color, $entries);
return $this->json($result, $result['success'] ? 200 : 422);
}
public function update(Request $request, string $id, string $tid): Response
{
$body = $request->jsonBody();
$name = trim((string) ($body['name'] ?? ''));
$description = trim((string) ($body['description'] ?? ''));
$color = trim((string) ($body['color'] ?? '#3B82F6'));
$entries = $body['entries'] ?? [];
if (!$name) {
return $this->json(['error' => 'اسم القالب مطلوب'], 400);
}
$db = \App\Core\App::getInstance()->db();
$ts = date('Y-m-d H:i:s');
$db->update('sa_pool_zone_templates', [
'name' => $name,
'description' => $description ?: null,
'color' => $color,
'updated_at' => $ts,
], 'id = ?', [(int) $tid]);
// Replace entries
$db->delete('sa_pool_zone_template_entries', 'template_id = ?', [(int) $tid]);
foreach ($entries as $entry) {
$zoneJson = $entry['zone_selection_json'] ?? '{}';
if (is_array($zoneJson)) {
$zoneJson = json_encode($zoneJson, JSON_UNESCAPED_UNICODE);
}
$db->insert('sa_pool_zone_template_entries', [
'template_id' => (int) $tid,
'day_of_week' => (int) ($entry['day_of_week'] ?? 0),
'start_time' => $entry['start_time'] ?? '09:00:00',
'end_time' => $entry['end_time'] ?? '10:00:00',
'zone_selection_type' => $entry['zone_selection_type'] ?? 'all',
'zone_selection_json' => $zoneJson,
'assignment_type' => $entry['assignment_type'] ?? 'training',
'group_id' => !empty($entry['group_id']) ? (int) $entry['group_id'] : null,
'coach_id' => !empty($entry['coach_id']) ? (int) $entry['coach_id'] : null,
'label' => $entry['label'] ?? $name,
'notes' => $entry['notes'] ?? null,
'created_at' => $ts,
'updated_at' => $ts,
]);
}
return $this->json(['success' => true]);
}
public function delete(Request $request, string $id, string $tid): Response
{
$result = PoolGridTemplateService::deleteTemplate((int) $tid);
return $this->json($result, $result['success'] ? 200 : 422);
}
public function expand(Request $request, string $id, string $tid): Response
{
$body = $request->jsonBody();
$fromDate = trim((string) ($body['from_date'] ?? ''));
$toDate = trim((string) ($body['to_date'] ?? ''));
if (!$fromDate || !$toDate) {
return $this->json(['error' => 'التواريخ مطلوبة'], 400);
}
if ($fromDate > $toDate) {
return $this->json(['error' => 'تاريخ البداية يجب أن يكون قبل النهاية'], 400);
}
// Max 60 days to prevent accidental mass generation
$daysDiff = (strtotime($toDate) - strtotime($fromDate)) / 86400;
if ($daysDiff > 60) {
return $this->json(['error' => 'الحد الأقصى 60 يوم للتوسيع'], 400);
}
$result = PoolGridTemplateService::expandTemplate((int) $tid, $fromDate, $toDate);
return $this->json($result, $result['success'] ? 200 : 422);
}
public function rollback(Request $request, string $id, string $tid, string $rid): Response
{
$result = PoolGridTemplateService::rollbackRun((int) $rid);
return $this->json($result, $result['success'] ? 200 : 422);
}
}
......@@ -155,4 +155,12 @@ return [
['POST', '/api/sa/pool-grid/{id:\d+}/clear', 'SportsActivity\Controllers\Api\PoolGridApiController@clear', ['auth', 'csrf'], 'sa.pool-grid.manage'],
['POST', '/api/sa/pool-grid/{id:\d+}/update-grid', 'SportsActivity\Controllers\Api\PoolGridApiController@updateGrid', ['auth', 'csrf'], 'sa.pool-grid.manage'],
['POST', '/api/sa/pool-grid/{id:\d+}/copy-slot', 'SportsActivity\Controllers\Api\PoolGridApiController@copySlot', ['auth', 'csrf'], 'sa.pool-grid.manage'],
// Pool Grid Templates
['GET', '/api/sa/pool-grid/{id:\d+}/templates', 'SportsActivity\Controllers\Api\PoolGridTemplateApiController@list', ['auth'], 'sa.pool-grid.manage'],
['POST', '/api/sa/pool-grid/{id:\d+}/templates', 'SportsActivity\Controllers\Api\PoolGridTemplateApiController@store', ['auth', 'csrf'], 'sa.pool-grid.manage'],
['POST', '/api/sa/pool-grid/{id:\d+}/templates/{tid:\d+}', 'SportsActivity\Controllers\Api\PoolGridTemplateApiController@update', ['auth', 'csrf'], 'sa.pool-grid.manage'],
['POST', '/api/sa/pool-grid/{id:\d+}/templates/{tid:\d+}/delete', 'SportsActivity\Controllers\Api\PoolGridTemplateApiController@delete', ['auth', 'csrf'], 'sa.pool-grid.manage'],
['POST', '/api/sa/pool-grid/{id:\d+}/templates/{tid:\d+}/expand', 'SportsActivity\Controllers\Api\PoolGridTemplateApiController@expand', ['auth', 'csrf'], 'sa.pool-grid.manage'],
['POST', '/api/sa/pool-grid/{id:\d+}/templates/{tid:\d+}/runs/{rid:\d+}/rollback', 'SportsActivity\Controllers\Api\PoolGridTemplateApiController@rollback', ['auth', 'csrf'], 'sa.pool-grid.manage'],
];
<?php
declare(strict_types=1);
return [
'up' => "CREATE TABLE `sa_pool_zone_templates` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`facility_id` BIGINT UNSIGNED NOT NULL,
`name` VARCHAR(200) NOT NULL,
`description` TEXT NULL,
`color` VARCHAR(20) NULL DEFAULT '#3B82F6',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_by` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_sa_pzt_facility` (`facility_id`, `is_active`),
CONSTRAINT `fk_sa_pzt_facility` FOREIGN KEY (`facility_id`) REFERENCES `sa_facilities`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
'down' => "DROP TABLE IF EXISTS `sa_pool_zone_templates`;",
];
<?php
declare(strict_types=1);
return [
'up' => "CREATE TABLE `sa_pool_zone_template_entries` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`template_id` BIGINT UNSIGNED NOT NULL,
`day_of_week` TINYINT UNSIGNED NOT NULL COMMENT '0=Sunday...6=Saturday',
`start_time` TIME NOT NULL,
`end_time` TIME NOT NULL,
`zone_selection_type` VARCHAR(20) NOT NULL COMMENT 'cells, row, column, all',
`zone_selection_json` JSON NOT NULL,
`assignment_type` VARCHAR(20) NOT NULL COMMENT 'training, blocked, maintenance, hourly',
`group_id` BIGINT UNSIGNED NULL,
`coach_id` BIGINT UNSIGNED NULL,
`label` VARCHAR(200) NULL,
`notes` TEXT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_sa_pzte_template` (`template_id`),
INDEX `idx_sa_pzte_day` (`day_of_week`, `start_time`),
CONSTRAINT `fk_sa_pzte_template` FOREIGN KEY (`template_id`) REFERENCES `sa_pool_zone_templates`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_sa_pzte_group` FOREIGN KEY (`group_id`) REFERENCES `sa_groups`(`id`) ON DELETE SET NULL,
CONSTRAINT `fk_sa_pzte_coach` FOREIGN KEY (`coach_id`) REFERENCES `sa_coaches`(`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
'down' => "DROP TABLE IF EXISTS `sa_pool_zone_template_entries`;",
];
<?php
declare(strict_types=1);
return [
'up' => "CREATE TABLE `sa_pool_zone_template_runs` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`template_id` BIGINT UNSIGNED NOT NULL,
`date_from` DATE NOT NULL,
`date_to` DATE NOT NULL,
`generated_count` INT UNSIGNED NOT NULL DEFAULT 0,
`skipped_count` INT UNSIGNED NOT NULL DEFAULT 0,
`status` VARCHAR(20) NOT NULL DEFAULT 'completed' COMMENT 'completed, rolled_back',
`rolled_back_at` TIMESTAMP NULL,
`rolled_back_by` BIGINT UNSIGNED NULL,
`created_by` BIGINT UNSIGNED NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_sa_pztr_template` (`template_id`),
CONSTRAINT `fk_sa_pztr_template` FOREIGN KEY (`template_id`) REFERENCES `sa_pool_zone_templates`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
'down' => "DROP TABLE IF EXISTS `sa_pool_zone_template_runs`;",
];
<?php
declare(strict_types=1);
return [
'up' => "ALTER TABLE `sa_pool_zone_bookings`
ADD COLUMN `template_run_id` BIGINT UNSIGNED NULL AFTER `notes`,
ADD INDEX `idx_sa_pzb_run` (`template_run_id`);",
'down' => "ALTER TABLE `sa_pool_zone_bookings`
DROP INDEX `idx_sa_pzb_run`,
DROP COLUMN `template_run_id`;",
];
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment