diff --git a/.gitfox/lint.yaml b/.gitfox/lint.yaml index efc29f54e4..8877426f71 100644 --- a/.gitfox/lint.yaml +++ b/.gitfox/lint.yaml @@ -12,13 +12,13 @@ spec: container: image: hub.zentao.net/ci/git:2.45.2 script: - - git diff --name-only --diff-filter=d ${DRONE_COMMIT_AFTER} ${DRONE_COMMIT_BEFORE} | tee .changes + - git diff --name-only --diff-filter=d ${GITFOX_COMMIT_AFTER} ${GITFOX_COMMIT_BEFORE} | tee .changes - | cat > sonar-project.properties <batchSetPost($fields); + { + $fields = 'assignedTo,mailto,comment,pri,type,status,deadline'; + $bug = $this->loadModel('bug')->getByID($bugID); + $this->batchSetPost($fields, $bug); $control = $this->loadController('bug', 'confirm'); $control->confirm($bugID); @@ -33,6 +34,6 @@ class bugConfirmEntry extends entry $bug = $this->loadModel('bug')->getById($bugID); return $this->send(200, $this->format($bug, 'openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,reviewedBy:user,reviewedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,deleted:bool,mailto:userList')); - } + } } diff --git a/api/v1/entries/bugresolve.php b/api/v1/entries/bugresolve.php index d11425e8ab..45ced63ed6 100644 --- a/api/v1/entries/bugresolve.php +++ b/api/v1/entries/bugresolve.php @@ -28,7 +28,7 @@ class bugResolveEntry extends entry $data = $this->getData(); if(!$data) return $this->send400('error'); - if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); + if(isset($data->result) and $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); $bug = $this->loadModel('bug')->getByID($bugID); diff --git a/api/v1/entries/executioncases.php b/api/v1/entries/executioncases.php index e5f4991700..51afb3ddd3 100644 --- a/api/v1/entries/executioncases.php +++ b/api/v1/entries/executioncases.php @@ -24,7 +24,7 @@ class executionCasesEntry extends entry if(empty($executionID)) return $this->sendError(400, 'Need execution id.'); $control = $this->loadController('execution', 'testcase'); - $control->testcase($executionID, $this->param('product', 0), $this->param('branch', 0), $this->param('status', 'all'), $this->param('module', 0), $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1)); + $control->testcase($executionID, (int)$this->param('product', 0), $this->param('branch', 'all'), $this->param('status', 'all'), 0, (int)$this->param('module', 0), $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1)); $data = $this->getData(); diff --git a/api/v1/entries/executions.php b/api/v1/entries/executions.php index d0dfecf930..5ddc623228 100644 --- a/api/v1/entries/executions.php +++ b/api/v1/entries/executions.php @@ -127,18 +127,18 @@ class executionsEntry extends entry if(isset($data->result) and $data->result == 'fail') return $this->sendError(400, $data->message); $account = $this->app->user->account; - $projects = $data->data->projects; $dropMenu = array('involved' => array(), 'other' => array(), 'closed' => array()); - foreach($data->data->executions as $projectID => $projectExecutions) + foreach($data->data->projects as $projectID => $projectName) { - foreach($projectExecutions as $execution) + if(!isset($data->data->projectExecutions->$projectID)) continue; + + foreach($data->data->projectExecutions->$projectID as $execution) { if(helper::diffDate(date('Y-m-d'), $execution->end) > 0) $execution->delay = true; $teams = $execution->teams; $execution = $this->filterFields($execution, 'id,project,model,type,name,code,status,PM,delay'); - $projectName = zget($projects, $execution->project, ''); - if($projectName) $execution->name = $projectName . '/' . $execution->name; + $execution->name = $projectName . '/' . $execution->name; if($execution->status == 'closed') { diff --git a/api/v1/entries/feedback.php b/api/v1/entries/feedback.php index 53358bbfa9..22b54424b6 100644 --- a/api/v1/entries/feedback.php +++ b/api/v1/entries/feedback.php @@ -36,7 +36,7 @@ class feedbackEntry extends entry if(!$data or !isset($data->status)) return $this->send400('error'); if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); - $feedback->actions = $this->loadModel('action')->processActionForAPI($data->data->actions, $data->data->users, $this->lang->feedback); + $feedback->actions = $this->loadModel('action')->processActionForAPI($data->data->history, $data->data->users, $this->lang->feedback); return $this->send(200, $this->format($feedback, 'activatedDate:time,openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,mailto:userList,resolvedBy:user,resolvedDate:time,closedBy:user,closedDate:time,lastEditedBy:user,lastEditedDate:time,deadline:date,deleted:bool')); } @@ -51,7 +51,7 @@ class feedbackEntry extends entry { $oldFeedback = $this->loadModel('feedback')->getById($feedbackID); - $fields = 'module,product,type,title,public,desc,status,feedbackBy,notifyEmail,notify,uid'; + $fields = 'module,product,type,title,public,desc,status,feedbackBy,notifyEmail,notify,uid,pri'; $this->batchSetPost($fields, $oldFeedback); $control = $this->loadController('feedback', 'edit'); diff --git a/api/v1/entries/feedbacks.php b/api/v1/entries/feedbacks.php index c144f169bd..0fdb2297c5 100644 --- a/api/v1/entries/feedbacks.php +++ b/api/v1/entries/feedbacks.php @@ -22,6 +22,7 @@ class feedbacksEntry extends entry if(strpos(strtolower($this->param('fields')), 'moduleandproduct') !== false) return $this->getModuleAndProduct(); $control = $this->loadController('feedback', 'admin'); + $control->session->set('feedbackProduct', 0); $control->admin($this->param('status', 'unclosed'), 0, $this->param('orderBy', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1)); $data = $this->getData(); @@ -54,7 +55,7 @@ class feedbacksEntry extends entry */ public function post() { - $fields = 'module,product,type,title,public,desc,status,feedbackBy,notify,uid'; + $fields = 'module,product,type,title,public,desc,status,feedbackBy,notify,uid,pri'; $this->batchSetPost($fields); $this->setPost('notifyEmail', $this->request('notifyEmail', '')); diff --git a/api/v1/entries/modules.php b/api/v1/entries/modules.php index a1aef82d82..0fd69f6578 100644 --- a/api/v1/entries/modules.php +++ b/api/v1/entries/modules.php @@ -23,7 +23,7 @@ class modulesEntry extends entry $objectID = $this->param('id', ''); if(!$objectType or !$objectID) return $this->sendError(400, 'Need id and type.'); - if(!in_array($objectType, array('story', 'task', 'bug', 'case'))) return $this->sendError(400, 'Type is not allowed'); + if(!in_array($objectType, array('story', 'task', 'bug', 'case', 'feedback', 'product'))) return $this->sendError(400, 'Type is not allowed'); if($objectType == 'task') { diff --git a/api/v1/entries/project.php b/api/v1/entries/project.php index 8d816a5fb8..498c762e3e 100644 --- a/api/v1/entries/project.php +++ b/api/v1/entries/project.php @@ -31,6 +31,7 @@ class projectEntry extends entry if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); $project = $this->format($data->data->project, 'openedBy:user,openedDate:time,lastEditedBy:user,lastEditedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,realBegan:date,realEnd:date,PM:user,whitelist:userList,deleted:bool'); + if(!$project->multiple) $project->executionID = $this->loadModel('execution')->getNoMultipleID($projectID); $this->loadModel('testcase'); $project->caseReview = ($this->config->testcase->needReview or !empty($this->config->testcase->forceReview)); diff --git a/api/v1/entries/projectcases.php b/api/v1/entries/projectcases.php index 06b2d93551..bde6a547f4 100644 --- a/api/v1/entries/projectcases.php +++ b/api/v1/entries/projectcases.php @@ -27,7 +27,7 @@ class projectCasesEntry extends entry $this->app->session->set('project', $projectID, $this->app->tab); $control = $this->loadController('project', 'testcase'); - $control->testcase($projectID, $this->param('product', 0), $this->param('branch', 0), $this->param('status', 'all'), 0, $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1)); + $control->testcase($projectID, $this->param('product', 0), $this->param('branch', 'all'), $this->param('status', 'all'), 0, $this->param('caseType', ''), $this->param('order', 'id_desc'), 0, $this->param('limit', 20), $this->param('page', 1)); $data = $this->getData(); diff --git a/api/v1/entries/releases.php b/api/v1/entries/releases.php index d8a837b751..334f0975d6 100644 --- a/api/v1/entries/releases.php +++ b/api/v1/entries/releases.php @@ -24,7 +24,7 @@ class releasesEntry extends entry if(empty($productID)) return $this->sendError(400, 'Need product id.'); $control = $this->loadController('release', 'browse'); - $control->browse($productID, $this->param('branch', 0), $this->param('status', 'all'), $this->param('order', 't1.date_desc'), 0, $this->param('limit', 20), $this->param('page', 1)); + $control->browse($productID, $this->param('branch', 'all'), $this->param('status', 'all'), $this->param('order', 't1.date_desc'), '', 0, $this->param('limit', 20), $this->param('page', 1)); /* Response */ $data = $this->getData(); diff --git a/api/v1/entries/reports.php b/api/v1/entries/reports.php index 180db9199d..f68b23e28d 100644 --- a/api/v1/entries/reports.php +++ b/api/v1/entries/reports.php @@ -162,9 +162,9 @@ class reportsEntry extends entry $newProject->id = $project->id; $newProject->name = $project->name; $newProject->status = $project->status; - $newProject->progress = round($project->hours->progress, 1); - $newProject->totalConsumed = round($project->hours->totalConsumed, 1); - $newProject->totalLeft = round($project->hours->totalLeft, 1); + $newProject->progress = round($project->progress, 1); + $newProject->totalConsumed = round($project->totalConsumed, 1); + $newProject->totalLeft = round($project->totalLeft, 1); if(isset($project->delay)) $newProject->delay = $project->delay; $statusList['all']['total'] += 1; @@ -206,9 +206,9 @@ class reportsEntry extends entry $newExecution->id = $execution->id; $newExecution->name = $execution->name; $newExecution->status = $execution->status; - $newExecution->progress = round($execution->hours->progress, 1); - $newExecution->totalConsumed = round($execution->hours->totalConsumed, 1); - $newExecution->totalLeft = round($execution->hours->totalLeft, 1); + $newExecution->progress = round($execution->progress, 1); + $newExecution->totalConsumed = round($execution->totalConsumed, 1); + $newExecution->totalLeft = round($execution->totalLeft, 1); if(isset($execution->delay)) $newExecution->delay = $execution->delay; $statusList['all']['total'] += 1; diff --git a/api/v1/entries/stories.php b/api/v1/entries/stories.php index c28ef766ed..8e41d74611 100644 --- a/api/v1/entries/stories.php +++ b/api/v1/entries/stories.php @@ -33,6 +33,7 @@ class storiesEntry extends entry $stories = $data->data->stories; $pager = $data->data->pager; $requirements = $this->loadModel('story')->getRequirements($productID); + if(!empty($requirements)) $requirements = $this->story->getByList(array_keys($requirements)); $result = array(); $this->loadModel('product'); diff --git a/api/v1/entries/story.php b/api/v1/entries/story.php index c3bc801424..ac62b3fac2 100644 --- a/api/v1/entries/story.php +++ b/api/v1/entries/story.php @@ -96,7 +96,7 @@ class storyEntry extends entry $oldStory = $this->loadModel('story')->getByID($storyID); /* Set $_POST variables. */ - $fields = 'title,product,parent,reviewer,type,plan,module,source,sourceNote,category,pri,estimate,mailto,keywords,uid,stage,notifyEmail'; + $fields = 'title,product,parent,reviewer,type,plan,module,source,sourceNote,category,pri,estimate,mailto,keywords,uid,stage,notifyEmail,status'; $this->batchSetPost($fields, $oldStory); $control = $this->loadController('story', 'edit'); diff --git a/api/v1/entries/storyactive.php b/api/v1/entries/storyactive.php index 9337cda0a4..e623146be2 100644 --- a/api/v1/entries/storyactive.php +++ b/api/v1/entries/storyactive.php @@ -29,8 +29,8 @@ class storyActiveEntry extends entry $control->activate($storyID); $data = $this->getData(); - if(!$data or !isset($data->status)) return $this->send400('error'); - if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); + if(!$data || !isset($data->result)) return $this->send400('error'); + if($data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); $story = $this->loadModel('story')->getByID($storyID); diff --git a/api/v1/entries/storyassignto.php b/api/v1/entries/storyassignto.php index 84cd2395b6..d0e2586115 100644 --- a/api/v1/entries/storyassignto.php +++ b/api/v1/entries/storyassignto.php @@ -28,7 +28,7 @@ class storyAssignToEntry extends entry $data = $this->getData(); if(!$data) return $this->send400('error'); - if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); + if(isset($data->result) and $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); $story = $this->loadModel('story')->getByID($storyID); diff --git a/api/v1/entries/storychange.php b/api/v1/entries/storychange.php index 9b2acb2c1c..b27f3f3df0 100644 --- a/api/v1/entries/storychange.php +++ b/api/v1/entries/storychange.php @@ -26,10 +26,12 @@ class storyChangeEntry extends entry $this->batchSetPost($fields); $fields = 'title,spec,verify'; $this->batchSetPost($fields, $oldStory); + $this->setPost('status', 'reviewing'); /* If reviewer is not post, set needNotReview. */ if(!$this->request('reviewer')) { + $this->setPost('status', $oldStory->status); $this->setPost('reviewer', array()); $this->setPost('needNotReview', 1); } @@ -37,11 +39,12 @@ class storyChangeEntry extends entry $control = $this->loadController('story', 'change'); $this->requireFields('title'); - $control->change($storyID); + $control->change($storyID, '', $oldStory->type); $data = $this->getData(); - if(!$data or !isset($data->status)) return $this->send400('error'); - if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); + if(!$data) return $this->send400('error'); + if(isset($data->result) && $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); + if(isset($data->status) && $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); $story = $this->loadModel('story')->getByID($storyID); diff --git a/api/v1/entries/storyclose.php b/api/v1/entries/storyclose.php index 1b32185e85..69d4c9aa63 100644 --- a/api/v1/entries/storyclose.php +++ b/api/v1/entries/storyclose.php @@ -28,7 +28,7 @@ class storyCloseEntry extends entry $data = $this->getData(); if(!$data) return $this->send400('error'); - if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); + if(isset($data->result) && $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); $story = $this->loadModel('story')->getByID($storyID); diff --git a/api/v1/entries/storyrecall.php b/api/v1/entries/storyrecall.php index 93b5c120f4..f55a6b5baf 100644 --- a/api/v1/entries/storyrecall.php +++ b/api/v1/entries/storyrecall.php @@ -21,10 +21,9 @@ class storyRecallEntry extends entry public function delete($storyID) { $control = $this->loadController('story', 'recall'); - $control->recall($storyID); + $control->recall($storyID, 'view', 'yes', $this->param('type', 'story')); $this->getData(); return $this->sendSuccess(200, 'success'); } } - diff --git a/api/v1/entries/task.php b/api/v1/entries/task.php index f2efe7ef4d..36aa382914 100644 --- a/api/v1/entries/task.php +++ b/api/v1/entries/task.php @@ -33,10 +33,11 @@ class taskEntry extends entry $task = $data->data->task; if(!empty($task->children)) $task->children = array_values((array)$task->children); - if($task->parent > 0) $task->parentPri = $this->dao->select('pri')->from(TABLE_TASK)->where('id')->eq($task->parent)->fetch('pri'); + if($task->parent > 0) $task->parentPri = $this->dao->findById($task->parent)->from(TABLE_TASK)->fetch('pri'); /* Set execution name */ - $task->executionName = $data->data->execution->name; + $task->executionName = $data->data->execution->name; + $task->executionStatus = $data->data->execution->status; /* Set module title */ $moduleTitle = ''; @@ -87,8 +88,6 @@ class taskEntry extends entry $task->preAndNext['pre'] = $preAndNext->pre ? $preAndNext->pre->id : ''; $task->preAndNext['next'] = $preAndNext->next ? $preAndNext->next->id : ''; - $execution = $this->loadModel('project')->getByID($task->execution, 'execution,sprint'); - $task->executionStatus = $execution->status; return $this->send(200, $this->format($task, 'deadline:date,openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,realStarted:time,finishedBy:user,finishedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,lastEditedBy:user,lastEditedDate:time,deleted:bool,mailto:userList')); } diff --git a/api/v1/entries/taskbatchcreate.php b/api/v1/entries/taskbatchcreate.php index 551106f188..212628943c 100644 --- a/api/v1/entries/taskbatchcreate.php +++ b/api/v1/entries/taskbatchcreate.php @@ -79,7 +79,7 @@ class taskBatchCreateEntry extends entry if(!$data) return $this->send400('error'); if(isset($data->data->result) and $data->data->result == 'fail') return $this->sendError(400, $data->data->message); - $tasks = $this->loadModel('task')->getByIdList($data->idList); + $tasks = $this->loadModel('task')->getByIdList((array)$data->idList); return $this->send(200, array('task' => $tasks)); } } diff --git a/api/v1/entries/taskrecordestimate.php b/api/v1/entries/taskrecordestimate.php index 835a04c02b..fbb608a05a 100644 --- a/api/v1/entries/taskrecordestimate.php +++ b/api/v1/entries/taskrecordestimate.php @@ -50,27 +50,16 @@ class taskRecordEstimateEntry extends entry */ public function post($taskID) { - if($this->loadModel('effort')) - { - $fields = 'id,dates,consumed,left,objectType,objectID,work'; - $this->batchSetPost($fields); - $control = $this->loadController('effort', 'createForObject'); - $control->createForObject('task', $taskID); - } - else - { - $fields = 'id,dates,consumed,left,work'; - $this->batchSetPost($fields); - $control = $this->loadController('task', 'recordWorkhour'); - $control->recordWorkhour($taskID); - } + $fields = 'date,consumed,left,work'; + $this->batchSetPost($fields); + $control = $this->loadController('task', 'recordWorkhour'); + $control->recordWorkhour($taskID); $data = $this->getData(); if(!$data) return $this->send400('error'); - if(isset($data->status) and $data->status == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); + if(isset($data->result) and $data->result == 'fail') return $this->sendError(zget($data, 'code', 400), $data->message); $task = $this->loadModel('task')->getById($taskID); - return $this->send(200, $this->format($task, 'deadline:date,openedBy:user,openedDate:time,assignedTo:user,assignedDate:time,realStarted:time,finishedBy:user,finishedDate:time,closedBy:user,closedDate:time,canceledBy:user,canceledDate:time,lastEditedBy:user,lastEditedDate:time,deleted:bool,mailto:userList')); } } diff --git a/api/v1/entries/testcase.php b/api/v1/entries/testcase.php index 74b4d6c59e..76a704d5f2 100644 --- a/api/v1/entries/testcase.php +++ b/api/v1/entries/testcase.php @@ -30,6 +30,19 @@ class testcaseEntry extends entry $case = $data->case; $case->steps = (isset($case->steps) and !empty($case->steps)) ? array_values(get_object_vars((object)$case->steps)) : array(); + if(!empty($case->steps)) + { + foreach($case->steps as &$step) + { + foreach(array('step', 'desc', 'expect') as $field) + { + if(isset($step->$field) && $step->$field != '') + { + $step->$field = str_replace(array("&quot;", "&#039;", "&lt;", "&gt;"), array('"', "'", "<", ">"), $step->$field); + } + } + } + } return $this->send(200, $this->format($case, 'openedBy:user,openedDate:time,lastEditedBy:user,lastEditedDate:time,lastRunDate:time,scriptedDate:date,reviewedBy:user,reviewedDate:date,steps:array,deleted:bool')); } diff --git a/api/v1/entries/user.php b/api/v1/entries/user.php index d23813ed22..4abba3a5a8 100644 --- a/api/v1/entries/user.php +++ b/api/v1/entries/user.php @@ -166,9 +166,9 @@ class userEntry extends entry { $myExecutions['owner'] = array(); $myExecutions['other'] = array(); - foreach($data->data->executions as $projectID => $projectExecutions) + foreach($data->data->projectExecutions as $executionList) { - foreach($projectExecutions as $execution) + foreach($executionList as $execution) { if($execution->status == 'done' or $execution->status == 'closed') continue; diff --git a/config/config.php b/config/config.php index d8055fe7b0..881171f525 100644 --- a/config/config.php +++ b/config/config.php @@ -16,7 +16,7 @@ if(!class_exists('config')){class config{}} if(!function_exists('getWebRoot')){function getWebRoot(){}} /* 基本设置。Basic settings. */ -$config->version = '20.5'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it. +$config->version = '20.6'; // ZenTaoPHP的版本。 The version of ZenTaoPHP. Don't change it. $config->liteVersion = '1.2'; // 迅捷版版本。 The version of Lite. $config->charset = 'UTF-8'; // ZenTaoPHP的编码。 The encoding of ZenTaoPHP. $config->cookieLife = time() + 2592000; // Cookie的生存时间。The cookie life time. diff --git a/config/zentaopms.php b/config/zentaopms.php index 2b115b19da..5e064f9620 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -756,7 +756,7 @@ $config->db->sqliteBlacklist = array('sqlite_queue', 'cron'); $config->hasDropmenuApps = array('program', 'project', 'product', 'execution', 'qa', 'admin', 'bi', 'feedback', 'demandpool'); $config->hasBranchMenuModules = array('product', 'story', 'release', 'bug', 'testcase', 'testtask', 'branch', 'tree'); $config->excludeBranchMenu = array('product-dashboard', 'product-view'); -$config->excludeDropmenuList = array('program-browse', 'program-productview', 'program-kanban', 'product-all', 'product-index', 'product-kanban', 'project-kanban', 'execution-all', 'execution-executionkanban', 'project-browse', 'project-batchedit', 'product-batchedit', 'admin-index', 'product-create', 'project-create', 'execution-create', 'program-create', 'execution-batchedit', 'metric-preview', 'metric-browse', 'metric-view', 'metriclib-browse', 'qa-index', 'caselib-create', 'feedback-batchedit', 'feedback-batchclose', 'feedback-showimport', 'dimension-browse', 'dataview-browse', 'dataview-query'); +$config->excludeDropmenuList = array('program-browse', 'program-productview', 'program-kanban', 'product-all', 'product-index', 'product-kanban', 'project-kanban', 'execution-all', 'execution-executionkanban', 'project-browse', 'project-batchedit', 'product-batchedit', 'admin-index', 'product-create', 'project-create', 'execution-create', 'program-create', 'execution-batchedit', 'metric-preview', 'metric-browse', 'metric-view', 'metriclib-browse', 'qa-index', 'caselib-create', 'feedback-batchedit', 'feedback-batchclose', 'feedback-showimport', 'dimension-browse', 'dataview-browse', 'dataview-query', 'ticket-batchedit', 'ticket-batchfinish', 'ticket-batchactivate'); $config->hasSwitcherModules = array('design'); $config->hasSwitcherMethods = array('project-bug', 'project-testcase', 'execution-bug', 'execution-testcase', 'testtask-cases', 'testtask-view', 'testtask-report', 'testtask-groupcase', 'testtask-linkcase'); $config->excludeSwitcherList = array(); diff --git a/db/demo.sql b/db/demo.sql index 02a117a3a2..605c5ee105 100644 --- a/db/demo.sql +++ b/db/demo.sql @@ -418,11 +418,11 @@ REPLACE INTO `zt_product` (`id`, `program`, `name`, `code`, `shadow`, `bind`, `l (2, 1, '企业内部工时管理系统', '', 0, '0', 0, 'normal', 'normal', '', '', 'productManager', '', 'productManager', '', '', 'open', '', '', '', NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 'admin', '2024-04-02 14:30:55', '20.0.beta2', NULL, 10, 'rnd', '0'); REPLACE INTO `zt_productplan` (`id`, `product`, `branch`, `parent`, `title`, `status`, `desc`, `begin`, `end`, `finishedDate`, `closedDate`, `order`, `closedReason`, `createdBy`, `createdDate`, `deleted`) VALUES (1, 1, '0', 0, '1.0版本', 'wait', '

开发出企业网站1.0版本。

', '2030-01-01', '2030-01-01', NULL, NULL, '0', '', 'admin', '2024-04-02 14:42:47', '0'); -REPLACE INTO `zt_project` (`id`, `project`, `charter`, `model`, `type`, `category`, `lifetime`, `budget`, `budgetUnit`, `attribute`, `percent`, `milestone`, `output`, `auth`, `parent`, `path`, `grade`, `name`, `code`, `hasProduct`, `begin`, `end`, `firstEnd`, `realBegan`, `realEnd`, `days`, `status`, `subStatus`, `pri`, `desc`, `version`, `parentVersion`, `planDuration`, `realDuration`, `progress`, `estimate`, `left`, `consumed`, `teamCount`, `market`, `openedBy`, `openedDate`, `openedVersion`, `lastEditedBy`, `lastEditedDate`, `closedBy`, `closedDate`, `closedReason`, `canceledBy`, `canceledDate`, `suspendedDate`, `PO`, `PM`, `QD`, `RD`, `team`, `acl`, `whitelist`, `order`, `vision`, `stageBy`, `displayCards`, `fluidBoard`, `multiple`, `parallel`, `colWidth`, `minColWidth`, `maxColWidth`, `deleted`) VALUES -(1, 0, 0, '', 'program', '', '', '', 'CNY', '', 0, '0', NULL, '', 0, ',1,', 1, '企业管理', '', 1, '2024-04-02', '2059-12-31', NULL, '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:25:12', '', '', NULL, '', NULL, '', '', NULL, NULL, '', '0', '', '', '', 'open', 'admin,projectManager', 5, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), -(2, 0, 0, 'scrum', 'project', '', '', '', 'CNY', '', 0, '0', NULL, 'extend', 1, ',1,2,', 2, '企业管理系统', '', 1, '2024-04-02', '2059-12-31', '2059-12-31', '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 3, 0, 'admin', '2024-04-02 14:34:13', '', 'admin', '2024-04-02 14:34:13', '', NULL, '', '', NULL, NULL, '', 'projectManager', '', '', '企业管理系统', 'open', '', 10, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), -(3, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', 2, ',2,3,', 1, '企业网站第一期', '', 1, '2024-04-02', '2024-04-15', '2024-04-15', '2024-04-02', NULL, 10, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 0, 0, 'admin', '2024-04-02 14:35:31', '20.0.beta2', 'admin', '2024-04-02 14:35:31', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', '', 'productManager', '企业网站第一期 ', 'open', '', 15, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), -(4, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', 2, ',2,4,', 1, '企业网站第二期', '', 1, '2024-04-02', '2024-05-02', NULL, NULL, NULL, 23, 'wait', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:36:20', '20.0.beta2', 'admin', '2024-04-02 14:36:20', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', 'admin', 'productManager', '企业网站第二期', 'open', '', 20, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'); +REPLACE INTO `zt_project` (`id`, `project`, `charter`, `model`, `type`, `category`, `lifetime`, `budget`, `budgetUnit`, `attribute`, `percent`, `milestone`, `output`, `auth`, `storyType`, `parent`, `path`, `grade`, `name`, `code`, `hasProduct`, `begin`, `end`, `firstEnd`, `realBegan`, `realEnd`, `days`, `status`, `subStatus`, `pri`, `desc`, `version`, `parentVersion`, `planDuration`, `realDuration`, `progress`, `estimate`, `left`, `consumed`, `teamCount`, `market`, `openedBy`, `openedDate`, `openedVersion`, `lastEditedBy`, `lastEditedDate`, `closedBy`, `closedDate`, `closedReason`, `canceledBy`, `canceledDate`, `suspendedDate`, `PO`, `PM`, `QD`, `RD`, `team`, `acl`, `whitelist`, `order`, `vision`, `stageBy`, `displayCards`, `fluidBoard`, `multiple`, `parallel`, `colWidth`, `minColWidth`, `maxColWidth`, `deleted`) VALUES +(1, 0, 0, '', 'program', '', '', '', 'CNY', '', 0, '0', NULL, '', '', 0, ',1,', 1, '企业管理', '', 1, '2024-04-02', '2059-12-31', NULL, '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:25:12', '', '', NULL, '', NULL, '', '', NULL, NULL, '', '0', '', '', '', 'open', 'admin,projectManager', 5, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), +(2, 0, 0, 'scrum', 'project', '', '', '', 'CNY', '', 0, '0', NULL, 'extend', 'story,requirement', 1, ',1,2,', 2, '企业管理系统', '', 1, '2024-04-02', '2059-12-31', '2059-12-31', '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 3, 0, 'admin', '2024-04-02 14:34:13', '', 'admin', '2024-04-02 14:34:13', '', NULL, '', '', NULL, NULL, '', 'projectManager', '', '', '企业管理系统', 'open', '', 10, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), +(3, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', '', 2, ',2,3,', 1, '企业网站第一期', '', 1, '2024-04-02', '2024-04-15', '2024-04-15', '2024-04-02', NULL, 10, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 0, 0, 'admin', '2024-04-02 14:35:31', '20.0.beta2', 'admin', '2024-04-02 14:35:31', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', '', 'productManager', '企业网站第一期 ', 'open', '', 15, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), +(4, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', '', 2, ',2,4,', 1, '企业网站第二期', '', 1, '2024-04-02', '2024-05-02', NULL, NULL, NULL, 23, 'wait', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:36:20', '20.0.beta2', 'admin', '2024-04-02 14:36:20', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', 'admin', 'productManager', '企业网站第二期', 'open', '', 20, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'); REPLACE INTO `zt_projectadmin` (`group`, `account`, `programs`, `projects`, `products`, `executions`) VALUES (1, 'admin', NULL, '2', NULL, NULL); REPLACE INTO `zt_projectcase` (`project`, `product`, `case`, `count`, `version`, `order`) VALUES diff --git a/db/endemo.sql b/db/endemo.sql index 02a117a3a2..906cdc4be1 100644 --- a/db/endemo.sql +++ b/db/endemo.sql @@ -418,11 +418,11 @@ REPLACE INTO `zt_product` (`id`, `program`, `name`, `code`, `shadow`, `bind`, `l (2, 1, '企业内部工时管理系统', '', 0, '0', 0, 'normal', 'normal', '', '', 'productManager', '', 'productManager', '', '', 'open', '', '', '', NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 'admin', '2024-04-02 14:30:55', '20.0.beta2', NULL, 10, 'rnd', '0'); REPLACE INTO `zt_productplan` (`id`, `product`, `branch`, `parent`, `title`, `status`, `desc`, `begin`, `end`, `finishedDate`, `closedDate`, `order`, `closedReason`, `createdBy`, `createdDate`, `deleted`) VALUES (1, 1, '0', 0, '1.0版本', 'wait', '

开发出企业网站1.0版本。

', '2030-01-01', '2030-01-01', NULL, NULL, '0', '', 'admin', '2024-04-02 14:42:47', '0'); -REPLACE INTO `zt_project` (`id`, `project`, `charter`, `model`, `type`, `category`, `lifetime`, `budget`, `budgetUnit`, `attribute`, `percent`, `milestone`, `output`, `auth`, `parent`, `path`, `grade`, `name`, `code`, `hasProduct`, `begin`, `end`, `firstEnd`, `realBegan`, `realEnd`, `days`, `status`, `subStatus`, `pri`, `desc`, `version`, `parentVersion`, `planDuration`, `realDuration`, `progress`, `estimate`, `left`, `consumed`, `teamCount`, `market`, `openedBy`, `openedDate`, `openedVersion`, `lastEditedBy`, `lastEditedDate`, `closedBy`, `closedDate`, `closedReason`, `canceledBy`, `canceledDate`, `suspendedDate`, `PO`, `PM`, `QD`, `RD`, `team`, `acl`, `whitelist`, `order`, `vision`, `stageBy`, `displayCards`, `fluidBoard`, `multiple`, `parallel`, `colWidth`, `minColWidth`, `maxColWidth`, `deleted`) VALUES -(1, 0, 0, '', 'program', '', '', '', 'CNY', '', 0, '0', NULL, '', 0, ',1,', 1, '企业管理', '', 1, '2024-04-02', '2059-12-31', NULL, '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:25:12', '', '', NULL, '', NULL, '', '', NULL, NULL, '', '0', '', '', '', 'open', 'admin,projectManager', 5, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), -(2, 0, 0, 'scrum', 'project', '', '', '', 'CNY', '', 0, '0', NULL, 'extend', 1, ',1,2,', 2, '企业管理系统', '', 1, '2024-04-02', '2059-12-31', '2059-12-31', '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 3, 0, 'admin', '2024-04-02 14:34:13', '', 'admin', '2024-04-02 14:34:13', '', NULL, '', '', NULL, NULL, '', 'projectManager', '', '', '企业管理系统', 'open', '', 10, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), -(3, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', 2, ',2,3,', 1, '企业网站第一期', '', 1, '2024-04-02', '2024-04-15', '2024-04-15', '2024-04-02', NULL, 10, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 0, 0, 'admin', '2024-04-02 14:35:31', '20.0.beta2', 'admin', '2024-04-02 14:35:31', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', '', 'productManager', '企业网站第一期 ', 'open', '', 15, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), -(4, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', 2, ',2,4,', 1, '企业网站第二期', '', 1, '2024-04-02', '2024-05-02', NULL, NULL, NULL, 23, 'wait', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:36:20', '20.0.beta2', 'admin', '2024-04-02 14:36:20', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', 'admin', 'productManager', '企业网站第二期', 'open', '', 20, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'); +REPLACE INTO `zt_project` (`id`, `project`, `charter`, `model`, `type`, `category`, `lifetime`, `budget`, `budgetUnit`, `attribute`, `percent`, `milestone`, `output`, `auth`, `storyType`, parent`, `path`, `grade`, `name`, `code`, `hasProduct`, `begin`, `end`, `firstEnd`, `realBegan`, `realEnd`, `days`, `status`, `subStatus`, `pri`, `desc`, `version`, `parentVersion`, `planDuration`, `realDuration`, `progress`, `estimate`, `left`, `consumed`, `teamCount`, `market`, `openedBy`, `openedDate`, `openedVersion`, `lastEditedBy`, `lastEditedDate`, `closedBy`, `closedDate`, `closedReason`, `canceledBy`, `canceledDate`, `suspendedDate`, `PO`, `PM`, `QD`, `RD`, `team`, `acl`, `whitelist`, `order`, `vision`, `stageBy`, `displayCards`, `fluidBoard`, `multiple`, `parallel`, `colWidth`, `minColWidth`, `maxColWidth`, `deleted`) VALUES +(1, 0, 0, '', 'program', '', '', '', 'CNY', '', 0, '0', NULL, '', '', 0, ',1,', 1, '企业管理', '', 1, '2024-04-02', '2059-12-31', NULL, '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:25:12', '', '', NULL, '', NULL, '', '', NULL, NULL, '', '0', '', '', '', 'open', 'admin,projectManager', 5, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), +(2, 0, 0, 'scrum', 'project', '', '', '', 'CNY', '', 0, '0', NULL, 'extend', 'story,requirement', 1, ',1,2,', 2, '企业管理系统', '', 1, '2024-04-02', '2059-12-31', '2059-12-31', '2024-04-02', NULL, 0, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 3, 0, 'admin', '2024-04-02 14:34:13', '', 'admin', '2024-04-02 14:34:13', '', NULL, '', '', NULL, NULL, '', 'projectManager', '', '', '企业管理系统', 'open', '', 10, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), +(3, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', '', 2, ',2,3,', 1, '企业网站第一期', '', 1, '2024-04-02', '2024-04-15', '2024-04-15', '2024-04-02', NULL, 10, 'doing', '', '1', '', 0, 0, 0, 0, 24.50, 52, 40, 13, 0, 0, 'admin', '2024-04-02 14:35:31', '20.0.beta2', 'admin', '2024-04-02 14:35:31', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', '', 'productManager', '企业网站第一期 ', 'open', '', 15, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'), +(4, 2, 0, '', 'sprint', '', 'long', '0', 'CNY', '', 0, '0', NULL, '', '', 2, ',2,4,', 1, '企业网站第二期', '', 1, '2024-04-02', '2024-05-02', NULL, NULL, NULL, 23, 'wait', '', '1', '', 0, 0, 0, 0, 0.00, 0, 0, 0, 0, 0, 'admin', '2024-04-02 14:36:20', '20.0.beta2', 'admin', '2024-04-02 14:36:20', '', NULL, '', '', NULL, NULL, 'productManager', 'projectManager', 'admin', 'productManager', '企业网站第二期', 'open', '', 20, 'rnd', 'product', 0, '0', '1', 0, 264, 200, 384, '0'); REPLACE INTO `zt_projectadmin` (`group`, `account`, `programs`, `projects`, `products`, `executions`) VALUES (1, 'admin', NULL, '2', NULL, NULL); REPLACE INTO `zt_projectcase` (`project`, `product`, `case`, `count`, `version`, `order`) VALUES diff --git a/db/standard/zentao20.6.sql b/db/standard/zentao20.6.sql new file mode 100644 index 0000000000..e6812a6ebc --- /dev/null +++ b/db/standard/zentao20.6.sql @@ -0,0 +1,4396 @@ +CREATE TABLE `zt_account` ( + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `type` varchar(255) NOT NULL DEFAULT '', + `provider` varchar(255) NOT NULL DEFAULT '', + `adminURI` varchar(255) NOT NULL DEFAULT '', + `account` varchar(255) NOT NULL DEFAULT '', + `password` varchar(255) NOT NULL DEFAULT '', + `email` varchar(255) NOT NULL DEFAULT '', + `mobile` varchar(255) NOT NULL DEFAULT '', + `extra` text DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `status` varchar(30) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `name` (`name`), + KEY `provider` (`provider`), + KEY `status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_acl` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL DEFAULT '', + `objectType` char(30) NOT NULL DEFAULT '', + `objectID` mediumint(9) NOT NULL DEFAULT 0, + `type` char(40) NOT NULL DEFAULT 'whitelist', + `source` char(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_action` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(30) NOT NULL DEFAULT '', + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` text DEFAULT NULL, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `actor` varchar(100) NOT NULL DEFAULT '', + `action` varchar(80) NOT NULL DEFAULT '', + `date` datetime DEFAULT NULL, + `comment` text DEFAULT NULL, + `extra` text DEFAULT NULL, + `read` enum('0','1') NOT NULL DEFAULT '0', + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `efforted` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `date` (`date`), + KEY `actor` (`actor`), + KEY `project` (`project`), + KEY `action` (`action`), + KEY `objectID` (`objectID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_actionrecent` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(30) NOT NULL DEFAULT '', + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` text DEFAULT NULL, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `actor` varchar(100) NOT NULL DEFAULT '', + `action` varchar(80) NOT NULL DEFAULT '', + `date` datetime DEFAULT NULL, + `comment` text DEFAULT NULL, + `extra` text DEFAULT NULL, + `read` enum('0','1') NOT NULL DEFAULT '0', + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `efforted` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `date` (`date`), + KEY `actor` (`actor`), + KEY `project` (`project`), + KEY `action` (`action`), + KEY `objectID` (`objectID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_activity` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `process` mediumint(9) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `optional` varchar(255) NOT NULL DEFAULT '', + `tailorNorm` varchar(255) NOT NULL DEFAULT '', + `content` mediumtext DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `order` mediumint(8) DEFAULT 0, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ai_assistant` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(30) NOT NULL, + `modelId` mediumint(8) unsigned NOT NULL, + `desc` text NOT NULL, + `systemMessage` text NOT NULL, + `greetings` text NOT NULL, + `icon` varchar(30) NOT NULL DEFAULT 'coding-1', + `enabled` enum('0','1') NOT NULL DEFAULT '1', + `createdDate` datetime NOT NULL, + `publishedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ai_message` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `appID` mediumint(8) unsigned NOT NULL, + `user` mediumint(8) unsigned NOT NULL, + `type` enum('req','res','ntf') NOT NULL, + `content` text NOT NULL, + `createdDate` datetime NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ai_miniprogram` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(30) NOT NULL, + `category` varchar(30) NOT NULL, + `desc` text DEFAULT NULL, + `model` mediumint(8) unsigned DEFAULT NULL, + `icon` varchar(30) NOT NULL DEFAULT 'writinghand-7', + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) NOT NULL, + `editedDate` datetime NOT NULL, + `published` enum('0','1') NOT NULL DEFAULT '0', + `publishedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `prompt` text NOT NULL, + `builtIn` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ai_miniprogramfield` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `appID` mediumint(8) unsigned NOT NULL, + `name` varchar(30) NOT NULL, + `type` enum('radio','checkbox','text','textarea') DEFAULT 'text', + `placeholder` text DEFAULT NULL, + `options` text DEFAULT NULL, + `required` enum('0','1') DEFAULT '1', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ai_miniprogramstar` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `appID` mediumint(8) unsigned NOT NULL, + `userID` mediumint(8) unsigned NOT NULL, + `createdDate` datetime NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `appID` (`appID`,`userID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ai_model` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(20) NOT NULL, + `vendor` varchar(20) NOT NULL, + `credentials` text NOT NULL, + `proxy` text DEFAULT NULL, + `name` varchar(20) DEFAULT NULL, + `desc` text DEFAULT NULL, + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) DEFAULT NULL, + `editedDate` datetime DEFAULT NULL, + `enabled` enum('0','1') NOT NULL DEFAULT '1', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ai_prompt` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(20) NOT NULL, + `desc` text DEFAULT NULL, + `model` mediumint(8) unsigned DEFAULT NULL, + `module` varchar(30) DEFAULT NULL, + `source` text DEFAULT NULL, + `targetForm` varchar(30) DEFAULT NULL, + `purpose` text DEFAULT NULL, + `elaboration` text DEFAULT NULL, + `role` text DEFAULT NULL, + `characterization` text DEFAULT NULL, + `status` enum('draft','active') NOT NULL DEFAULT 'draft', + `createdBy` varchar(30) NOT NULL, + `createdDate` datetime NOT NULL, + `editedBy` varchar(30) DEFAULT NULL, + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ai_promptrole` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(30) DEFAULT NULL, + `desc` text DEFAULT NULL, + `model` mediumint(8) unsigned DEFAULT NULL, + `role` text DEFAULT NULL, + `characterization` text DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_api` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `product` varchar(255) NOT NULL DEFAULT '', + `lib` int(11) unsigned NOT NULL DEFAULT 0, + `module` int(11) unsigned NOT NULL DEFAULT 0, + `title` varchar(100) NOT NULL DEFAULT '', + `path` varchar(255) NOT NULL DEFAULT '', + `protocol` varchar(10) NOT NULL DEFAULT '', + `method` varchar(10) NOT NULL DEFAULT '', + `requestType` varchar(100) NOT NULL DEFAULT '', + `responseType` varchar(100) NOT NULL DEFAULT '', + `status` varchar(20) NOT NULL DEFAULT '', + `owner` varchar(30) NOT NULL DEFAULT '0', + `desc` mediumtext DEFAULT NULL, + `version` smallint(6) unsigned NOT NULL DEFAULT 0, + `params` text DEFAULT NULL, + `paramsExample` text DEFAULT NULL, + `responseExample` text DEFAULT NULL, + `response` text DEFAULT NULL, + `commonParams` text DEFAULT NULL, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_api_lib_release` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `lib` int(11) unsigned NOT NULL DEFAULT 0, + `desc` varchar(255) NOT NULL DEFAULT '', + `version` varchar(255) NOT NULL DEFAULT '', + `snap` mediumtext DEFAULT NULL, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_apispec` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `doc` int(11) unsigned NOT NULL DEFAULT 0, + `module` int(11) unsigned NOT NULL DEFAULT 0, + `title` varchar(100) NOT NULL DEFAULT '', + `path` varchar(255) NOT NULL DEFAULT '', + `protocol` varchar(10) NOT NULL DEFAULT '', + `method` varchar(10) NOT NULL DEFAULT '', + `requestType` varchar(100) NOT NULL DEFAULT '', + `responseType` varchar(100) NOT NULL DEFAULT '', + `status` varchar(20) NOT NULL DEFAULT '', + `owner` varchar(255) NOT NULL DEFAULT '0', + `desc` mediumtext DEFAULT NULL, + `version` smallint(6) unsigned NOT NULL DEFAULT 0, + `params` text DEFAULT NULL, + `paramsExample` text DEFAULT NULL, + `responseExample` text DEFAULT NULL, + `response` text DEFAULT NULL, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_apistruct` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `lib` int(11) unsigned NOT NULL DEFAULT 0, + `name` varchar(30) NOT NULL DEFAULT '', + `type` varchar(50) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `version` smallint(6) unsigned NOT NULL DEFAULT 0, + `attribute` text DEFAULT NULL, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_apistruct_spec` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `type` varchar(50) NOT NULL DEFAULT '', + `desc` varchar(255) NOT NULL DEFAULT '', + `attribute` text DEFAULT NULL, + `version` smallint(6) unsigned NOT NULL DEFAULT 0, + `addedBy` varchar(30) NOT NULL DEFAULT '0', + `addedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_approval` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `flow` mediumint(8) NOT NULL DEFAULT 0, + `objectType` varchar(30) NOT NULL DEFAULT '', + `objectID` mediumint(9) NOT NULL DEFAULT 0, + `nodes` mediumtext DEFAULT NULL, + `version` mediumint(9) NOT NULL DEFAULT 0, + `status` varchar(20) NOT NULL DEFAULT 'doing', + `result` varchar(20) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `deleted` tinyint(4) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_approvalflow` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `code` varchar(100) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `version` mediumint(8) NOT NULL DEFAULT 1, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `type` varchar(30) NOT NULL DEFAULT '', + `deleted` tinyint(4) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_approvalflowobject` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `root` int(8) NOT NULL DEFAULT 0, + `flow` int(8) NOT NULL DEFAULT 0, + `objectType` char(30) NOT NULL DEFAULT '', + `objectID` mediumint(9) NOT NULL DEFAULT 0, + `extra` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_approvalflowspec` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `flow` mediumint(8) NOT NULL DEFAULT 0, + `version` mediumint(8) NOT NULL DEFAULT 0, + `nodes` mediumtext DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_approvalnode` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `approval` mediumint(8) NOT NULL DEFAULT 0, + `type` enum('review','cc') NOT NULL DEFAULT 'review', + `title` varchar(255) NOT NULL DEFAULT '', + `account` char(30) NOT NULL DEFAULT '', + `node` varchar(100) NOT NULL DEFAULT '', + `reviewType` varchar(100) NOT NULL DEFAULT 'manual', + `agentType` varchar(100) NOT NULL DEFAULT 'pass', + `multipleType` enum('and','or') NOT NULL DEFAULT 'and', + `percent` smallint(6) NOT NULL DEFAULT 0, + `needAll` enum('0','1') NOT NULL DEFAULT '0', + `solicit` enum('0','1') NOT NULL DEFAULT '0', + `prev` mediumtext DEFAULT NULL, + `next` mediumtext DEFAULT NULL, + `status` varchar(20) NOT NULL DEFAULT 'wait', + `result` varchar(10) NOT NULL DEFAULT '', + `date` date DEFAULT NULL, + `opinion` mediumtext DEFAULT NULL, + `extra` mediumtext DEFAULT NULL, + `revertTo` char(30) NOT NULL DEFAULT '', + `forwardBy` char(30) NOT NULL DEFAULT '', + `reviewedBy` char(30) NOT NULL DEFAULT '', + `reviewedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_reviewed_date` (`reviewedDate`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_approvalobject` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `approval` int(8) NOT NULL DEFAULT 0, + `objectType` char(30) NOT NULL DEFAULT '', + `objectID` mediumint(8) NOT NULL DEFAULT 0, + `extra` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_approvalrole` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `code` char(30) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `users` longtext DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_artifactrepo` ( + `id` smallint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(45) NOT NULL DEFAULT '', + `products` varchar(255) NOT NULL DEFAULT '', + `serverID` smallint(8) NOT NULL DEFAULT 0, + `repoName` varchar(45) NOT NULL DEFAULT '', + `format` varchar(10) NOT NULL DEFAULT '', + `type` char(7) NOT NULL DEFAULT '', + `status` varchar(10) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` tinyint(4) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_assetlib` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `type` varchar(255) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `order` smallint(5) unsigned NOT NULL DEFAULT 0, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_attend` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL DEFAULT '', + `date` date DEFAULT NULL, + `signIn` time DEFAULT NULL, + `signOut` time DEFAULT NULL, + `status` varchar(30) NOT NULL DEFAULT '', + `ip` varchar(100) NOT NULL DEFAULT '', + `device` varchar(30) NOT NULL DEFAULT '', + `client` varchar(20) NOT NULL DEFAULT '', + `manualIn` time DEFAULT NULL, + `manualOut` time DEFAULT NULL, + `reason` varchar(30) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `reviewStatus` varchar(30) DEFAULT NULL, + `reviewedBy` char(30) NOT NULL DEFAULT '', + `reviewedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `attend` (`date`,`account`), + KEY `account` (`account`), + KEY `date` (`date`), + KEY `status` (`status`), + KEY `reason` (`reason`), + KEY `reviewStatus` (`reviewStatus`), + KEY `reviewedBy` (`reviewedBy`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_attendstat` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL DEFAULT '', + `month` char(10) NOT NULL DEFAULT '', + `normal` decimal(12,2) NOT NULL DEFAULT 0.00, + `late` decimal(12,2) NOT NULL DEFAULT 0.00, + `early` decimal(12,2) NOT NULL DEFAULT 0.00, + `absent` decimal(12,2) NOT NULL DEFAULT 0.00, + `trip` decimal(12,2) NOT NULL DEFAULT 0.00, + `egress` decimal(12,2) NOT NULL DEFAULT 0.00, + `lieu` decimal(12,2) NOT NULL DEFAULT 0.00, + `paidLeave` decimal(12,2) NOT NULL DEFAULT 0.00, + `unpaidLeave` decimal(12,2) NOT NULL DEFAULT 0.00, + `timeOvertime` decimal(12,2) NOT NULL DEFAULT 0.00, + `restOvertime` decimal(12,2) NOT NULL DEFAULT 0.00, + `holidayOvertime` decimal(12,2) NOT NULL DEFAULT 0.00, + `deserve` decimal(12,2) NOT NULL DEFAULT 0.00, + `actual` decimal(12,2) NOT NULL DEFAULT 0.00, + `status` char(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `attend` (`month`,`account`), + KEY `account` (`account`), + KEY `month` (`month`), + KEY `status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_auditcl` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `model` char(30) NOT NULL DEFAULT 'waterfall', + `practiceArea` char(30) NOT NULL DEFAULT '', + `type` char(30) NOT NULL DEFAULT '', + `title` varchar(255) NOT NULL DEFAULT '', + `objectType` char(30) NOT NULL DEFAULT '', + `objectID` int(11) NOT NULL DEFAULT 0, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_auditplan` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `dateType` char(30) NOT NULL DEFAULT '', + `config` text DEFAULT NULL, + `objectID` mediumint(9) NOT NULL DEFAULT 0, + `objectType` char(30) NOT NULL DEFAULT '', + `process` mediumint(9) NOT NULL DEFAULT 0, + `processType` char(30) NOT NULL DEFAULT '', + `checkDate` date DEFAULT NULL, + `checkedBy` varchar(30) NOT NULL DEFAULT '', + `realCheckDate` date DEFAULT NULL, + `result` char(30) NOT NULL DEFAULT '', + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `checkBy` varchar(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_auditresult` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `auditplan` mediumint(8) NOT NULL DEFAULT 0, + `listID` mediumint(8) NOT NULL DEFAULT 0, + `result` char(30) NOT NULL DEFAULT '', + `checkedBy` varchar(30) NOT NULL DEFAULT '', + `checkedDate` date DEFAULT NULL, + `comment` text DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_automation` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `node` int(11) unsigned NOT NULL DEFAULT 0, + `product` int(11) unsigned NOT NULL DEFAULT 0, + `scriptPath` varchar(255) NOT NULL DEFAULT '', + `shell` mediumtext DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_basicmeas` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `purpose` varchar(50) NOT NULL DEFAULT '', + `scope` char(30) NOT NULL DEFAULT '', + `object` char(30) NOT NULL DEFAULT '', + `name` varchar(90) NOT NULL DEFAULT '', + `code` char(30) NOT NULL DEFAULT '', + `unit` varchar(10) NOT NULL DEFAULT '', + `configure` text DEFAULT NULL, + `params` text DEFAULT NULL, + `definition` text DEFAULT NULL, + `source` varchar(255) NOT NULL DEFAULT '', + `collectType` varchar(30) NOT NULL DEFAULT '', + `collectConf` text DEFAULT NULL, + `execTime` varchar(30) NOT NULL DEFAULT '', + `collectedBy` varchar(10) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `order` mediumint(8) unsigned NOT NULL DEFAULT 0, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `code` (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_block` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL DEFAULT '', + `dashboard` varchar(20) NOT NULL DEFAULT '', + `module` varchar(20) NOT NULL DEFAULT '', + `title` varchar(100) NOT NULL DEFAULT '', + `block` varchar(30) NOT NULL DEFAULT '', + `code` varchar(30) NOT NULL DEFAULT '', + `width` enum('1','2','3') NOT NULL DEFAULT '1', + `height` smallint(6) unsigned NOT NULL DEFAULT 3, + `left` enum('0','1','2') NOT NULL DEFAULT '0', + `top` smallint(5) unsigned NOT NULL DEFAULT 0, + `params` text DEFAULT NULL, + `hidden` tinyint(1) unsigned NOT NULL DEFAULT 0, + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + PRIMARY KEY (`id`), + KEY `account` (`account`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_branch` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `default` enum('0','1') NOT NULL DEFAULT '0', + `status` enum('active','closed') NOT NULL DEFAULT 'active', + `desc` varchar(255) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `closedDate` date DEFAULT NULL, + `order` smallint(6) unsigned NOT NULL DEFAULT 0, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_budget` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `stage` char(30) NOT NULL DEFAULT '', + `subject` mediumint(8) NOT NULL DEFAULT 0, + `amount` char(30) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_bug` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `injection` mediumint(8) unsigned NOT NULL DEFAULT 0, + `identify` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` mediumint(8) unsigned NOT NULL DEFAULT 0, + `module` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `plan` mediumint(8) unsigned NOT NULL DEFAULT 0, + `story` mediumint(8) unsigned NOT NULL DEFAULT 0, + `storyVersion` smallint(6) NOT NULL DEFAULT 1, + `task` mediumint(8) unsigned NOT NULL DEFAULT 0, + `toTask` mediumint(8) unsigned NOT NULL DEFAULT 0, + `toStory` mediumint(8) NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `keywords` varchar(255) NOT NULL DEFAULT '', + `severity` tinyint(4) NOT NULL DEFAULT 0, + `pri` tinyint(3) unsigned NOT NULL DEFAULT 0, + `type` varchar(30) NOT NULL DEFAULT '', + `os` varchar(255) NOT NULL DEFAULT '', + `browser` varchar(255) NOT NULL DEFAULT '', + `hardware` varchar(30) NOT NULL DEFAULT '', + `found` varchar(30) NOT NULL DEFAULT '', + `steps` mediumtext DEFAULT NULL, + `status` enum('active','resolved','closed') NOT NULL DEFAULT 'active', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL DEFAULT '', + `confirmed` tinyint(1) NOT NULL DEFAULT 0, + `activatedCount` smallint(6) NOT NULL DEFAULT 0, + `activatedDate` datetime DEFAULT NULL, + `feedbackBy` varchar(100) NOT NULL DEFAULT '', + `notifyEmail` varchar(100) NOT NULL DEFAULT '', + `mailto` text DEFAULT NULL, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + `openedBuild` varchar(255) NOT NULL DEFAULT '', + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `deadline` date DEFAULT NULL, + `resolvedBy` varchar(30) NOT NULL DEFAULT '', + `resolution` varchar(30) NOT NULL DEFAULT '', + `resolvedBuild` varchar(30) NOT NULL DEFAULT '', + `resolvedDate` datetime DEFAULT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `duplicateBug` mediumint(8) unsigned NOT NULL DEFAULT 0, + `relatedBug` varchar(255) NOT NULL DEFAULT '', + `case` mediumint(8) unsigned NOT NULL DEFAULT 0, + `caseVersion` smallint(6) NOT NULL DEFAULT 1, + `feedback` mediumint(8) unsigned NOT NULL DEFAULT 0, + `result` mediumint(8) unsigned NOT NULL DEFAULT 0, + `repo` mediumint(8) unsigned NOT NULL DEFAULT 0, + `mr` mediumint(8) unsigned NOT NULL DEFAULT 0, + `entry` text DEFAULT NULL, + `lines` varchar(10) NOT NULL DEFAULT '', + `v1` varchar(255) NOT NULL DEFAULT '', + `v2` varchar(255) NOT NULL DEFAULT '', + `repoType` varchar(30) NOT NULL DEFAULT '', + `issueKey` varchar(50) NOT NULL DEFAULT '', + `testtask` mediumint(8) unsigned NOT NULL DEFAULT 0, + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`), + KEY `status` (`status`), + KEY `plan` (`plan`), + KEY `story` (`story`), + KEY `case` (`case`), + KEY `toStory` (`toStory`), + KEY `result` (`result`), + KEY `assignedTo` (`assignedTo`), + KEY `deleted` (`deleted`), + KEY `project` (`project`), + KEY `product_status_deleted` (`product`,`status`,`deleted`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_build` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` varchar(255) NOT NULL DEFAULT '0', + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `builds` varchar(255) NOT NULL DEFAULT '', + `name` char(150) NOT NULL DEFAULT '', + `scmPath` char(255) NOT NULL DEFAULT '', + `filePath` char(255) NOT NULL DEFAULT '', + `date` date DEFAULT NULL, + `stories` text DEFAULT NULL, + `bugs` text DEFAULT NULL, + `artifactRepoID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `builder` char(30) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_burn` ( + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `task` mediumint(8) unsigned NOT NULL DEFAULT 0, + `date` date NOT NULL, + `estimate` float NOT NULL DEFAULT 0, + `left` float NOT NULL DEFAULT 0, + `consumed` float NOT NULL DEFAULT 0, + `storyPoint` float NOT NULL DEFAULT 0, + PRIMARY KEY (`execution`,`date`,`task`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_case` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` mediumint(8) unsigned NOT NULL DEFAULT 0, + `lib` mediumint(8) unsigned NOT NULL DEFAULT 0, + `module` mediumint(8) unsigned NOT NULL DEFAULT 0, + `path` mediumint(8) unsigned NOT NULL DEFAULT 0, + `story` mediumint(30) unsigned NOT NULL DEFAULT 0, + `storyVersion` smallint(6) NOT NULL DEFAULT 1, + `title` varchar(255) NOT NULL DEFAULT '', + `precondition` text DEFAULT NULL, + `keywords` varchar(255) NOT NULL DEFAULT '', + `pri` tinyint(3) unsigned NOT NULL DEFAULT 3, + `type` char(30) NOT NULL DEFAULT '1', + `auto` varchar(10) NOT NULL DEFAULT 'no', + `frame` varchar(10) NOT NULL DEFAULT '', + `stage` varchar(255) NOT NULL DEFAULT '', + `howRun` varchar(30) NOT NULL DEFAULT '', + `script` longtext DEFAULT NULL, + `scriptedBy` varchar(30) NOT NULL DEFAULT '', + `scriptedDate` date DEFAULT NULL, + `scriptStatus` varchar(30) NOT NULL DEFAULT '', + `scriptLocation` varchar(255) NOT NULL DEFAULT '', + `status` char(30) NOT NULL DEFAULT '1', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL DEFAULT '', + `frequency` enum('1','2','3') NOT NULL DEFAULT '1', + `order` tinyint(30) unsigned NOT NULL DEFAULT 0, + `openedBy` char(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + `reviewedBy` varchar(255) NOT NULL DEFAULT '', + `reviewedDate` date DEFAULT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `version` tinyint(3) unsigned NOT NULL DEFAULT 0, + `linkCase` varchar(255) NOT NULL DEFAULT '', + `fromBug` mediumint(8) unsigned NOT NULL DEFAULT 0, + `fromCaseID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `fromCaseVersion` mediumint(8) unsigned NOT NULL DEFAULT 1, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `lastRunner` varchar(30) NOT NULL DEFAULT '', + `lastRunDate` datetime DEFAULT NULL, + `lastRunResult` char(30) NOT NULL DEFAULT '', + `scene` int(11) NOT NULL DEFAULT 0, + `sort` int(11) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `story` (`story`), + KEY `fromBug` (`fromBug`), + KEY `module` (`module`), + KEY `scene` (`scene`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_casestep` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `case` mediumint(8) unsigned NOT NULL DEFAULT 0, + `version` smallint(6) unsigned NOT NULL DEFAULT 0, + `type` varchar(10) NOT NULL DEFAULT 'step', + `desc` text DEFAULT NULL, + `expect` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `case` (`case`), + KEY `version` (`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_cfd` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `execution` int(8) NOT NULL DEFAULT 0, + `type` char(30) NOT NULL DEFAULT '', + `name` char(30) NOT NULL DEFAULT '', + `count` smallint(6) NOT NULL DEFAULT 0, + `date` date DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `execution_type_name_date` (`execution`,`type`,`name`,`date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_chart` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `code` varchar(255) NOT NULL DEFAULT '', + `driver` enum('mysql','duckdb') NOT NULL DEFAULT 'mysql', + `dimension` mediumint(8) unsigned NOT NULL DEFAULT 0, + `type` varchar(30) NOT NULL DEFAULT '', + `group` varchar(255) NOT NULL DEFAULT '', + `dataset` varchar(30) NOT NULL DEFAULT '0', + `desc` text DEFAULT NULL, + `settings` mediumtext DEFAULT NULL, + `filters` mediumtext DEFAULT NULL, + `step` tinyint(3) unsigned NOT NULL DEFAULT 0, + `fields` mediumtext DEFAULT NULL, + `langs` text DEFAULT NULL, + `sql` mediumtext DEFAULT NULL, + `stage` enum('draft','published') NOT NULL DEFAULT 'draft', + `builtin` tinyint(1) unsigned NOT NULL DEFAULT 0, + `objects` mediumtext DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` tinyint(4) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_charter` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `level` int(8) NOT NULL DEFAULT 0, + `category` char(30) NOT NULL DEFAULT '', + `market` varchar(30) NOT NULL DEFAULT '', + `check` enum('0','1') NOT NULL DEFAULT '0', + `appliedBy` char(30) NOT NULL DEFAULT '', + `appliedDate` datetime DEFAULT NULL, + `budget` char(30) NOT NULL DEFAULT '', + `budgetUnit` char(30) NOT NULL DEFAULT '', + `product` text DEFAULT NULL, + `roadmap` text DEFAULT NULL, + `spec` mediumtext DEFAULT NULL, + `status` char(30) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `charterFiles` text DEFAULT NULL, + `closedBy` char(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `closedReason` varchar(255) NOT NULL DEFAULT '', + `activatedBy` char(30) NOT NULL DEFAULT '', + `activatedDate` datetime DEFAULT NULL, + `reviewedBy` varchar(255) NOT NULL DEFAULT '', + `reviewedResult` char(30) NOT NULL DEFAULT '', + `reviewedDate` datetime DEFAULT NULL, + `meetingDate` date DEFAULT NULL, + `meetingLocation` varchar(255) NOT NULL DEFAULT '', + `meetingMinutes` mediumtext DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_cmcl` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` char(30) NOT NULL DEFAULT '', + `projectType` varchar(255) NOT NULL DEFAULT '', + `title` int(11) NOT NULL DEFAULT 0, + `contents` text DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT '', + `order` int(11) NOT NULL DEFAULT 0, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_company` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` char(120) DEFAULT NULL, + `phone` char(20) DEFAULT NULL, + `fax` char(20) DEFAULT NULL, + `address` char(120) DEFAULT NULL, + `zipcode` char(10) DEFAULT NULL, + `website` char(120) DEFAULT NULL, + `backyard` char(120) DEFAULT NULL, + `guest` enum('1','0') NOT NULL DEFAULT '0', + `admins` char(255) DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_compile` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL DEFAULT '', + `job` mediumint(8) unsigned NOT NULL DEFAULT 0, + `queue` mediumint(8) NOT NULL DEFAULT 0, + `status` varchar(255) NOT NULL DEFAULT '', + `logs` text DEFAULT NULL, + `atTime` varchar(10) NOT NULL DEFAULT '', + `testtask` mediumint(8) unsigned NOT NULL DEFAULT 0, + `tag` varchar(255) NOT NULL DEFAULT '', + `times` tinyint(3) unsigned NOT NULL DEFAULT 0, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `updateDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_config` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `vision` varchar(10) NOT NULL DEFAULT '', + `owner` char(30) NOT NULL DEFAULT '', + `module` varchar(30) NOT NULL DEFAULT '', + `section` char(30) NOT NULL DEFAULT '', + `key` char(30) NOT NULL DEFAULT '', + `value` longtext DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unique` (`vision`,`owner`,`module`,`section`,`key`), + KEY `vision` (`vision`), + KEY `owner` (`owner`), + KEY `module` (`module`), + KEY `key` (`key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_cron` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `m` varchar(20) NOT NULL DEFAULT '', + `h` varchar(20) NOT NULL DEFAULT '', + `dom` varchar(20) NOT NULL DEFAULT '', + `mon` varchar(20) NOT NULL DEFAULT '', + `dow` varchar(20) NOT NULL DEFAULT '', + `command` text DEFAULT NULL, + `remark` varchar(255) NOT NULL DEFAULT '', + `type` varchar(20) NOT NULL DEFAULT '', + `buildin` tinyint(1) NOT NULL DEFAULT 0, + `status` varchar(20) NOT NULL DEFAULT '', + `lastTime` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `lastTime` (`lastTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_dashboard` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `dimension` int(8) NOT NULL DEFAULT 0, + `module` mediumint(8) NOT NULL DEFAULT 0, + `desc` mediumtext DEFAULT NULL, + `layout` mediumtext DEFAULT NULL, + `filters` mediumtext DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `deleted` tinyint(4) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_dataset` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(155) NOT NULL DEFAULT '', + `sql` text DEFAULT NULL, + `fields` mediumtext DEFAULT NULL, + `objects` mediumtext DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `deleted` tinyint(4) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_dataview` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `group` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(155) NOT NULL DEFAULT '', + `code` varchar(50) NOT NULL DEFAULT '', + `view` varchar(57) NOT NULL DEFAULT '', + `sql` text DEFAULT NULL, + `fields` mediumtext DEFAULT NULL, + `langs` text DEFAULT NULL, + `objects` mediumtext DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` tinyint(4) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_demand` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `pool` int(8) NOT NULL DEFAULT 0, + `module` int(8) NOT NULL DEFAULT 0, + `product` varchar(255) NOT NULL DEFAULT '', + `parent` mediumint(8) NOT NULL DEFAULT 0, + `pri` char(30) NOT NULL DEFAULT '', + `category` char(30) NOT NULL DEFAULT '', + `source` char(30) NOT NULL DEFAULT '', + `sourceNote` varchar(255) NOT NULL DEFAULT '', + `title` varchar(255) NOT NULL DEFAULT '', + `feedbackedBy` varchar(255) NOT NULL DEFAULT '', + `email` varchar(255) NOT NULL DEFAULT '', + `assignedTo` char(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `reviewedBy` text DEFAULT NULL, + `reviewedDate` datetime DEFAULT NULL, + `status` char(30) NOT NULL DEFAULT '', + `stage` enum('wait','distributed','inroadmap','incharter','developing','delivering','delivered','closed') NOT NULL DEFAULT 'wait', + `duration` char(30) NOT NULL DEFAULT '', + `BSA` char(30) NOT NULL DEFAULT '', + `story` mediumint(8) NOT NULL DEFAULT 0, + `roadmap` mediumint(8) NOT NULL DEFAULT 0, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `mailto` text DEFAULT NULL, + `duplicateDemand` mediumint(8) DEFAULT NULL, + `childDemands` varchar(255) NOT NULL DEFAULT '', + `version` varchar(255) NOT NULL DEFAULT '', + `parentVersion` smallint(6) NOT NULL DEFAULT 0, + `vision` varchar(255) NOT NULL DEFAULT 'or', + `color` varchar(255) NOT NULL DEFAULT '', + `changedBy` char(30) NOT NULL DEFAULT '', + `changedDate` datetime DEFAULT NULL, + `closedBy` char(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `closedReason` varchar(30) NOT NULL DEFAULT '', + `submitedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `activatedDate` datetime DEFAULT NULL, + `distributedBy` varchar(30) NOT NULL DEFAULT '', + `distributedDate` datetime DEFAULT NULL, + `feedback` mediumint(9) NOT NULL DEFAULT 0, + `keywords` varchar(255) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_demandpool` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `status` char(30) NOT NULL DEFAULT '', + `products` varchar(255) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `owner` text DEFAULT NULL, + `reviewer` text DEFAULT NULL, + `acl` char(30) DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_demandreview` ( + `demand` mediumint(9) NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 0, + `reviewer` varchar(30) NOT NULL DEFAULT '', + `result` varchar(30) NOT NULL DEFAULT '', + `reviewDate` datetime DEFAULT NULL, + UNIQUE KEY `demand` (`demand`,`version`,`reviewer`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_demandspec` ( + `demand` mediumint(9) NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `spec` mediumtext DEFAULT NULL, + `verify` mediumtext DEFAULT NULL, + `files` text DEFAULT NULL, + UNIQUE KEY `demand` (`demand`,`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_deploy` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `begin` datetime DEFAULT NULL, + `end` datetime DEFAULT NULL, + `name` varchar(255) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `status` varchar(20) NOT NULL DEFAULT '', + `owner` char(30) NOT NULL DEFAULT '', + `members` text DEFAULT NULL, + `notify` text DEFAULT NULL, + `cases` text DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `result` varchar(20) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_deployproduct` ( + `deploy` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `release` mediumint(8) unsigned NOT NULL DEFAULT 0, + `package` varchar(255) NOT NULL DEFAULT '', + UNIQUE KEY `deploy_product_release` (`deploy`,`product`,`release`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_deployscope` ( + `deploy` mediumint(8) unsigned NOT NULL DEFAULT 0, + `service` mediumint(8) unsigned NOT NULL DEFAULT 0, + `hosts` text DEFAULT NULL, + `remove` text DEFAULT NULL, + `add` text DEFAULT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_deploystep` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `deploy` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `begin` datetime DEFAULT NULL, + `end` datetime DEFAULT NULL, + `stage` varchar(30) NOT NULL DEFAULT '', + `content` text DEFAULT NULL, + `status` varchar(30) NOT NULL DEFAULT '', + `assignedTo` char(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `finishedBy` char(30) NOT NULL DEFAULT '', + `finishedDate` datetime DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_dept` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` char(60) NOT NULL DEFAULT '', + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `path` char(255) NOT NULL DEFAULT '', + `grade` tinyint(3) unsigned NOT NULL DEFAULT 0, + `order` smallint(6) unsigned NOT NULL DEFAULT 0, + `position` char(30) NOT NULL DEFAULT '', + `function` char(255) NOT NULL DEFAULT '', + `manager` char(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + KEY `parent` (`parent`), + KEY `path` (`path`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_design` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` varchar(255) NOT NULL DEFAULT '', + `product` varchar(255) NOT NULL DEFAULT '', + `commit` text DEFAULT NULL, + `commitedBy` varchar(30) NOT NULL DEFAULT '', + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `story` char(30) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `version` smallint(6) NOT NULL DEFAULT 0, + `type` char(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_designspec` ( + `design` mediumint(8) NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `files` varchar(255) NOT NULL DEFAULT '', + UNIQUE KEY `design` (`design`,`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_dimension` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(90) NOT NULL DEFAULT '', + `code` varchar(45) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `code` (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_doc` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `lib` varchar(30) NOT NULL DEFAULT '', + `template` varchar(30) NOT NULL DEFAULT '', + `templateType` varchar(30) NOT NULL DEFAULT '', + `chapterType` varchar(30) NOT NULL DEFAULT '', + `module` varchar(30) NOT NULL DEFAULT '', + `title` varchar(255) NOT NULL DEFAULT '', + `keywords` varchar(255) NOT NULL DEFAULT '', + `type` varchar(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT 'normal', + `parent` smallint(6) unsigned NOT NULL DEFAULT 0, + `path` char(255) NOT NULL DEFAULT '', + `grade` tinyint(3) unsigned NOT NULL DEFAULT 0, + `order` smallint(6) unsigned NOT NULL DEFAULT 0, + `views` smallint(6) unsigned NOT NULL DEFAULT 0, + `assetLib` mediumint(8) unsigned NOT NULL DEFAULT 0, + `assetLibType` varchar(30) NOT NULL DEFAULT '', + `from` mediumint(8) unsigned NOT NULL DEFAULT 0, + `fromVersion` smallint(6) NOT NULL DEFAULT 1, + `draft` longtext DEFAULT NULL, + `collects` smallint(6) unsigned NOT NULL DEFAULT 0, + `addedBy` varchar(30) NOT NULL DEFAULT '', + `addedDate` datetime DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `approvedDate` date DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `editingDate` text DEFAULT NULL, + `editedList` text DEFAULT NULL, + `mailto` text DEFAULT NULL, + `acl` varchar(10) NOT NULL DEFAULT 'open', + `groups` varchar(255) NOT NULL DEFAULT '', + `users` text DEFAULT NULL, + `version` smallint(6) unsigned NOT NULL DEFAULT 1, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`), + KEY `lib` (`lib`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_docaction` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `doc` mediumint(8) unsigned NOT NULL DEFAULT 0, + `action` varchar(80) NOT NULL DEFAULT '', + `actor` char(30) NOT NULL DEFAULT '', + `date` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `doc` (`doc`), + KEY `actor` (`actor`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_doccontent` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `doc` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `digest` varchar(255) NOT NULL DEFAULT '', + `content` longtext DEFAULT NULL, + `files` text DEFAULT NULL, + `type` varchar(10) NOT NULL DEFAULT '', + `version` smallint(6) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `doc_version` (`doc`,`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_doclib` ( + `id` smallint(6) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(30) NOT NULL DEFAULT '', + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(60) NOT NULL DEFAULT '', + `baseUrl` varchar(255) NOT NULL DEFAULT '', + `acl` varchar(10) NOT NULL DEFAULT 'open', + `groups` varchar(255) NOT NULL DEFAULT '', + `users` text DEFAULT NULL, + `main` enum('0','1') NOT NULL DEFAULT '0', + `collector` text DEFAULT NULL, + `desc` mediumtext DEFAULT NULL, + `order` tinyint(5) unsigned NOT NULL DEFAULT 0, + `addedBy` varchar(30) NOT NULL DEFAULT '', + `addedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `execution` (`execution`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_domain` ( + `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT, + `domain` varchar(255) NOT NULL DEFAULT '', + `adminURI` varchar(255) NOT NULL DEFAULT '', + `resolverURI` varchar(255) NOT NULL DEFAULT '', + `register` varchar(255) NOT NULL DEFAULT '', + `expiredDate` datetime DEFAULT NULL, + `renew` varchar(255) NOT NULL DEFAULT '', + `account` varchar(255) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `domain` (`domain`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_duckdbqueue` ( + `object` varchar(255) NOT NULL DEFAULT '', + `updatedTime` datetime DEFAULT NULL, + `syncTime` datetime DEFAULT NULL, + UNIQUE KEY `object` (`object`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_durationestimation` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `stage` mediumint(9) NOT NULL DEFAULT 0, + `workload` varchar(255) NOT NULL DEFAULT '', + `worktimeRate` varchar(255) NOT NULL DEFAULT '', + `people` varchar(255) NOT NULL DEFAULT '', + `startDate` date DEFAULT NULL, + `endDate` date DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_effort` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(30) NOT NULL DEFAULT '', + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` text DEFAULT NULL, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `account` varchar(30) NOT NULL DEFAULT '', + `work` text DEFAULT NULL, + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `date` date DEFAULT NULL, + `left` float NOT NULL DEFAULT 0, + `consumed` float NOT NULL DEFAULT 0, + `begin` smallint(4) unsigned zerofill NOT NULL DEFAULT 0000, + `end` smallint(4) unsigned zerofill NOT NULL DEFAULT 0000, + `extra` text DEFAULT NULL, + `order` tinyint(3) unsigned NOT NULL DEFAULT 0, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `execution` (`execution`), + KEY `objectID` (`objectID`), + KEY `date` (`date`), + KEY `account` (`account`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_entry` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL DEFAULT '', + `account` varchar(30) NOT NULL DEFAULT '', + `code` varchar(20) NOT NULL DEFAULT '', + `key` varchar(32) NOT NULL DEFAULT '', + `freePasswd` enum('0','1') NOT NULL DEFAULT '0', + `ip` varchar(100) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `calledTime` int(11) unsigned NOT NULL DEFAULT 0, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_expect` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `userID` mediumint(8) NOT NULL DEFAULT 0, + `project` mediumint(8) NOT NULL DEFAULT 0, + `expect` text DEFAULT NULL, + `progress` text DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_extension` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(150) NOT NULL DEFAULT '', + `code` varchar(30) NOT NULL DEFAULT '', + `version` varchar(50) NOT NULL DEFAULT '', + `author` varchar(100) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `license` text DEFAULT NULL, + `type` varchar(20) NOT NULL DEFAULT 'extension', + `site` varchar(150) NOT NULL DEFAULT '', + `zentaoCompatible` text DEFAULT NULL, + `installedTime` datetime DEFAULT NULL, + `depends` varchar(100) NOT NULL DEFAULT '', + `dirs` mediumtext DEFAULT NULL, + `files` mediumtext DEFAULT NULL, + `status` varchar(20) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `code` (`code`), + KEY `name` (`name`), + KEY `installedTime` (`installedTime`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_faq` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `module` mediumint(9) NOT NULL DEFAULT 0, + `product` mediumint(9) NOT NULL DEFAULT 0, + `question` varchar(255) NOT NULL DEFAULT '', + `answer` text DEFAULT NULL, + `addedtime` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_feedback` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `module` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `type` char(30) NOT NULL DEFAULT '', + `solution` char(30) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `pri` tinyint(3) unsigned NOT NULL DEFAULT 2, + `status` varchar(30) NOT NULL DEFAULT '', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `public` enum('0','1') NOT NULL DEFAULT '0', + `notify` enum('0','1') NOT NULL DEFAULT '0', + `notifyEmail` varchar(100) NOT NULL DEFAULT '', + `source` varchar(255) NOT NULL DEFAULT '', + `likes` text DEFAULT NULL, + `result` mediumint(8) unsigned NOT NULL DEFAULT 0, + `faq` mediumint(8) unsigned NOT NULL DEFAULT 0, + `openedBy` char(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + `reviewedBy` varchar(255) NOT NULL DEFAULT '', + `reviewedDate` datetime DEFAULT NULL, + `processedBy` char(30) NOT NULL DEFAULT '', + `processedDate` datetime DEFAULT NULL, + `closedBy` char(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `closedReason` varchar(30) NOT NULL DEFAULT '', + `editedBy` char(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `assignedTo` varchar(255) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `activatedBy` varchar(30) NOT NULL DEFAULT '', + `activatedDate` datetime DEFAULT NULL, + `feedbackBy` varchar(100) NOT NULL DEFAULT '', + `repeatFeedback` mediumint(8) NOT NULL DEFAULT 0, + `mailto` varchar(255) NOT NULL DEFAULT '', + `keywords` varchar(255) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_feedbackview` ( + `account` char(30) NOT NULL DEFAULT '', + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + UNIQUE KEY `account_product` (`account`,`product`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_file` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `pathname` char(100) NOT NULL DEFAULT '', + `title` varchar(255) NOT NULL DEFAULT '', + `extension` char(30) NOT NULL DEFAULT '', + `size` int(11) unsigned NOT NULL DEFAULT 0, + `objectType` char(30) NOT NULL DEFAULT '', + `objectID` mediumint(9) NOT NULL DEFAULT 0, + `addedBy` char(30) NOT NULL DEFAULT '', + `addedDate` datetime DEFAULT NULL, + `downloads` mediumint(8) unsigned NOT NULL DEFAULT 0, + `extra` varchar(255) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `objectType` (`objectType`), + KEY `objectID` (`objectID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_gapanalysis` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `account` varchar(30) NOT NULL DEFAULT '', + `role` varchar(20) NOT NULL DEFAULT '', + `analysis` mediumtext DEFAULT NULL, + `needTrain` enum('no','yes') NOT NULL DEFAULT 'no', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `project_account` (`project`,`account`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_group` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `name` char(30) NOT NULL DEFAULT '', + `role` char(30) NOT NULL DEFAULT '', + `desc` char(255) NOT NULL DEFAULT '', + `acl` text DEFAULT NULL, + `developer` enum('0','1') NOT NULL DEFAULT '1', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_grouppriv` ( + `group` mediumint(8) unsigned NOT NULL DEFAULT 0, + `module` char(30) NOT NULL DEFAULT '', + `method` char(30) NOT NULL DEFAULT '', + UNIQUE KEY `group` (`group`,`module`,`method`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_history` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `action` mediumint(8) unsigned NOT NULL DEFAULT 0, + `field` varchar(30) NOT NULL DEFAULT '', + `old` text DEFAULT NULL, + `new` text DEFAULT NULL, + `diff` mediumtext DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `action` (`action`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_holiday` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(30) NOT NULL DEFAULT '', + `type` enum('holiday','working') NOT NULL DEFAULT 'holiday', + `desc` text DEFAULT NULL, + `year` char(4) NOT NULL DEFAULT '', + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `year` (`year`), + KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_host` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `type` varchar(30) NOT NULL DEFAULT 'normal', + `hostType` varchar(30) NOT NULL DEFAULT '', + `mac` varchar(128) NOT NULL DEFAULT '', + `memory` varchar(30) NOT NULL DEFAULT '', + `diskSize` varchar(30) NOT NULL DEFAULT '', + `status` varchar(50) NOT NULL DEFAULT '', + `secret` varchar(50) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `tokenSN` varchar(50) NOT NULL DEFAULT '', + `tokenTime` datetime DEFAULT NULL, + `oldTokenSN` varchar(50) NOT NULL DEFAULT '', + `vsoft` varchar(30) NOT NULL DEFAULT '', + `heartbeat` datetime DEFAULT NULL, + `zap` varchar(10) NOT NULL DEFAULT '', + `provider` varchar(255) NOT NULL DEFAULT '', + `vnc` int(11) NOT NULL DEFAULT 0, + `ztf` int(11) NOT NULL DEFAULT 0, + `zd` int(11) NOT NULL DEFAULT 0, + `ssh` int(11) NOT NULL DEFAULT 0, + `parent` int(11) unsigned NOT NULL DEFAULT 0, + `image` int(11) unsigned NOT NULL DEFAULT 0, + `admin` smallint(5) unsigned NOT NULL DEFAULT 0, + `serverRoom` mediumint(8) unsigned NOT NULL DEFAULT 0, + `serverModel` varchar(256) NOT NULL DEFAULT '', + `hardwareType` varchar(64) NOT NULL DEFAULT '', + `cpuBrand` varchar(128) NOT NULL DEFAULT '', + `cpuModel` varchar(128) NOT NULL DEFAULT '', + `cpuNumber` varchar(16) NOT NULL DEFAULT '', + `cpuCores` varchar(30) NOT NULL DEFAULT '', + `intranet` varchar(128) NOT NULL DEFAULT '', + `extranet` varchar(128) NOT NULL DEFAULT '', + `osName` varchar(64) NOT NULL DEFAULT '', + `osVersion` varchar(64) NOT NULL DEFAULT '', + `group` varchar(128) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_chat` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `gid` char(40) NOT NULL DEFAULT '', + `name` varchar(60) NOT NULL DEFAULT '', + `type` varchar(20) NOT NULL DEFAULT 'group', + `admins` varchar(255) NOT NULL DEFAULT '', + `committers` varchar(255) NOT NULL DEFAULT '', + `subject` mediumint(8) unsigned NOT NULL DEFAULT 0, + `public` enum('0','1') NOT NULL DEFAULT '0', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `ownedBy` varchar(30) NOT NULL DEFAULT '', + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `mergedDate` datetime DEFAULT NULL, + `lastActiveTime` datetime DEFAULT NULL, + `lastMessage` int(11) unsigned NOT NULL DEFAULT 0, + `lastMessageIndex` int(11) unsigned NOT NULL DEFAULT 0, + `dismissDate` datetime DEFAULT NULL, + `pinnedMessages` text DEFAULT NULL, + `mergedChats` text DEFAULT NULL, + `adminInvite` enum('0','1') NOT NULL DEFAULT '0', + `avatar` text DEFAULT NULL, + `archiveDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `gid` (`gid`), + KEY `name` (`name`), + KEY `type` (`type`), + KEY `public` (`public`), + KEY `createdBy` (`createdBy`), + KEY `editedBy` (`editedBy`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_chat_message_index` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `gid` char(40) NOT NULL DEFAULT '', + `tableName` char(64) NOT NULL DEFAULT '', + `start` int(11) unsigned NOT NULL DEFAULT 0, + `end` int(11) unsigned NOT NULL DEFAULT 0, + `startIndex` int(11) unsigned NOT NULL DEFAULT 0, + `endIndex` int(11) unsigned NOT NULL DEFAULT 0, + `startDate` datetime DEFAULT NULL, + `endDate` datetime DEFAULT NULL, + `count` mediumint(8) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `chattable` (`gid`,`tableName`), + KEY `start` (`start`), + KEY `end` (`end`), + KEY `startDate` (`startDate`), + KEY `endDate` (`endDate`), + KEY `chatstartindex` (`gid`,`startIndex`), + KEY `chatendindex` (`gid`,`endIndex`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_chatuser` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `cgid` char(40) NOT NULL DEFAULT '', + `user` mediumint(8) NOT NULL DEFAULT 0, + `order` smallint(5) NOT NULL DEFAULT 0, + `star` enum('0','1') NOT NULL DEFAULT '0', + `hide` enum('0','1') NOT NULL DEFAULT '0', + `mute` enum('0','1') NOT NULL DEFAULT '0', + `freeze` enum('0','1') NOT NULL DEFAULT '0', + `join` datetime DEFAULT NULL, + `quit` datetime DEFAULT NULL, + `category` varchar(40) NOT NULL DEFAULT '', + `lastReadMessage` int(11) unsigned NOT NULL DEFAULT 0, + `lastReadMessageIndex` int(11) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `chatuser` (`cgid`,`user`), + KEY `cgid` (`cgid`), + KEY `user` (`user`), + KEY `order` (`order`), + KEY `star` (`star`), + KEY `hide` (`hide`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_client` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `version` char(30) NOT NULL DEFAULT '', + `desc` varchar(100) NOT NULL DEFAULT '', + `changeLog` text DEFAULT NULL, + `strategy` varchar(10) NOT NULL DEFAULT '', + `downloads` text DEFAULT NULL, + `createdDate` datetime DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `status` enum('released','wait') NOT NULL DEFAULT 'wait', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_conference` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `rid` char(40) NOT NULL DEFAULT '', + `cgid` char(40) NOT NULL DEFAULT '', + `status` enum('closed','open','notStarted','canceled') NOT NULL DEFAULT 'closed', + `participants` text DEFAULT NULL, + `subscribers` text DEFAULT NULL, + `invitee` text DEFAULT NULL, + `openedBy` mediumint(8) NOT NULL DEFAULT 0, + `openedDate` datetime DEFAULT NULL, + `topic` text DEFAULT NULL, + `startTime` datetime DEFAULT NULL, + `endTime` datetime DEFAULT NULL, + `password` char(20) NOT NULL DEFAULT '', + `type` enum('default','periodic','scheduled') NOT NULL DEFAULT 'default', + `number` char(20) NOT NULL DEFAULT '', + `note` text DEFAULT NULL, + `sentNotify` tinyint(1) NOT NULL DEFAULT 0, + `reminderTime` int(11) NOT NULL DEFAULT 0, + `moderators` text NOT NULL, + `isPrivate` enum('0','1') NOT NULL DEFAULT '0', + `isInner` enum('0','1') NOT NULL DEFAULT '1', + PRIMARY KEY (`id`), + KEY `status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_conferenceaction` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `rid` char(40) NOT NULL DEFAULT '', + `type` enum('create','invite','join','leave','close','publish') NOT NULL DEFAULT 'create', + `data` text DEFAULT NULL, + `user` mediumint(8) NOT NULL DEFAULT 0, + `date` datetime DEFAULT NULL, + `device` char(40) NOT NULL DEFAULT 'default', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_conferenceinvite` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `conferenceID` mediumint(8) unsigned NOT NULL, + `inviteeID` mediumint(8) unsigned NOT NULL, + `status` enum('pending','accepted','rejected') NOT NULL DEFAULT 'pending', + `createdDate` datetime DEFAULT NULL, + `updatedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `conference_user` (`conferenceID`,`inviteeID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_conferenceuser` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `conference` mediumint(8) NOT NULL DEFAULT 0, + `user` mediumint(8) NOT NULL DEFAULT 0, + `hide` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `conferenceuser` (`conference`,`user`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_message` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `gid` char(40) NOT NULL DEFAULT '', + `cgid` char(40) NOT NULL DEFAULT '', + `user` varchar(30) NOT NULL DEFAULT '', + `date` datetime DEFAULT NULL, + `index` int(11) unsigned NOT NULL DEFAULT 0, + `type` enum('normal','broadcast','notify','bulletin','botcommand') NOT NULL DEFAULT 'normal', + `content` text DEFAULT NULL, + `contentType` enum('text','plain','emotion','image','file','object','code') NOT NULL DEFAULT 'text', + `data` text DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `legacy` tinyint(1) NOT NULL DEFAULT 0, + `uniqueIndex` int(11) GENERATED ALWAYS AS (case when `legacy` = 1 then NULL when `cgid` = 'notification' then NULL else `index` end) STORED, + PRIMARY KEY (`id`), + UNIQUE KEY `uniqueIndexInChat` (`cgid`,`uniqueIndex`), + KEY `mgid` (`gid`), + KEY `mcgid` (`cgid`), + KEY `muser` (`user`), + KEY `mtype` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_message_backup` ( + `id` int(11) unsigned NOT NULL DEFAULT 0, + `gid` char(40) NOT NULL DEFAULT '', + `cgid` char(40) NOT NULL DEFAULT '', + `user` varchar(30) NOT NULL DEFAULT '', + `date` datetime DEFAULT NULL, + `index` int(11) unsigned NOT NULL DEFAULT 0, + `type` enum('normal','broadcast','notify') NOT NULL DEFAULT 'normal', + `content` text DEFAULT NULL, + `contentType` enum('text','plain','emotion','image','file','object','code') NOT NULL DEFAULT 'text', + `data` text DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_message_index` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `tableName` char(64) NOT NULL DEFAULT '', + `start` int(11) unsigned NOT NULL DEFAULT 0, + `end` int(11) unsigned NOT NULL DEFAULT 0, + `startDate` datetime DEFAULT NULL, + `endDate` datetime DEFAULT NULL, + `chats` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `tableName` (`tableName`), + KEY `start` (`start`), + KEY `end` (`end`), + KEY `startDate` (`startDate`), + KEY `endDate` (`endDate`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_messagestatus` ( + `user` mediumint(8) NOT NULL DEFAULT 0, + `message` int(11) unsigned NOT NULL DEFAULT 0, + `status` enum('waiting','sent','readed','deleted') NOT NULL DEFAULT 'waiting', + UNIQUE KEY `user` (`user`,`message`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_queue` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` char(30) NOT NULL DEFAULT '', + `content` text DEFAULT NULL, + `addDate` datetime DEFAULT NULL, + `processDate` datetime DEFAULT NULL, + `result` text DEFAULT NULL, + `status` char(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_im_userdevice` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `user` mediumint(8) NOT NULL DEFAULT 0, + `device` char(40) NOT NULL DEFAULT 'default', + `deviceID` char(40) NOT NULL DEFAULT '', + `token` char(64) NOT NULL DEFAULT '', + `validUntil` datetime DEFAULT NULL, + `lastLogin` datetime DEFAULT NULL, + `lastLogout` datetime DEFAULT NULL, + `online` tinyint(1) NOT NULL DEFAULT 0, + `version` char(10) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `userdevice` (`user`,`device`), + KEY `user` (`user`), + KEY `lastLogin` (`lastLogin`), + KEY `lastLogout` (`lastLogout`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_image` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `host` int(11) unsigned NOT NULL DEFAULT 0, + `name` varchar(64) NOT NULL DEFAULT '', + `localName` varchar(64) NOT NULL DEFAULT '', + `address` varchar(64) NOT NULL DEFAULT '', + `path` varchar(64) NOT NULL DEFAULT '', + `status` varchar(20) NOT NULL DEFAULT '', + `osName` varchar(32) NOT NULL DEFAULT '', + `from` varchar(10) NOT NULL DEFAULT 'zentao', + `memory` float unsigned NOT NULL DEFAULT 0, + `disk` float unsigned NOT NULL DEFAULT 0, + `fileSize` float unsigned NOT NULL DEFAULT 0, + `md5` varchar(64) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `restoreDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_instance` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `space` mediumint(8) unsigned NOT NULL DEFAULT 0, + `solution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` char(50) NOT NULL DEFAULT '', + `appID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `appName` char(50) NOT NULL DEFAULT '', + `appVersion` char(20) NOT NULL DEFAULT '', + `chart` char(50) NOT NULL DEFAULT '', + `logo` varchar(255) NOT NULL DEFAULT '', + `version` char(50) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `introduction` text DEFAULT NULL, + `source` char(20) NOT NULL DEFAULT '', + `channel` char(20) NOT NULL DEFAULT '', + `k8name` char(64) NOT NULL DEFAULT '', + `status` char(20) NOT NULL DEFAULT '', + `pinned` enum('0','1') NOT NULL DEFAULT '0', + `domain` char(255) NOT NULL DEFAULT '', + `smtpSnippetName` char(30) NOT NULL DEFAULT '', + `ldapSnippetName` char(30) NOT NULL DEFAULT '', + `ldapSettings` text DEFAULT NULL, + `dbSettings` text DEFAULT NULL, + `autoBackup` tinyint(1) NOT NULL DEFAULT 0, + `backupKeepDays` int(10) unsigned NOT NULL DEFAULT 1, + `autoRestore` tinyint(1) NOT NULL DEFAULT 0, + `env` text DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdAt` datetime DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `space` (`space`), + KEY `k8name` (`k8name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_intervention` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `activity` mediumint(8) NOT NULL DEFAULT 0, + `status` char(30) NOT NULL DEFAULT '', + `partake` text DEFAULT NULL, + `begin` date DEFAULT NULL, + `realBegin` date DEFAULT NULL, + `situation` varchar(255) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `project` (`project`,`activity`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_issue` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `resolvedBy` varchar(30) NOT NULL DEFAULT '', + `project` varchar(255) NOT NULL DEFAULT '', + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `pri` char(30) NOT NULL DEFAULT '', + `severity` char(30) NOT NULL DEFAULT '', + `type` char(30) NOT NULL DEFAULT '', + `activity` varchar(255) NOT NULL DEFAULT '', + `deadline` date DEFAULT NULL, + `resolution` char(30) NOT NULL DEFAULT '', + `resolutionComment` text DEFAULT NULL, + `objectID` varchar(255) NOT NULL DEFAULT '', + `resolvedDate` date DEFAULT NULL, + `status` varchar(30) NOT NULL DEFAULT '', + `owner` varchar(255) NOT NULL DEFAULT '', + `lib` mediumint(8) unsigned NOT NULL DEFAULT 0, + `from` mediumint(8) unsigned NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 1, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `activateBy` varchar(30) NOT NULL DEFAULT '', + `activateDate` date DEFAULT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` date DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `approvedDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_job` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(50) NOT NULL DEFAULT '', + `repo` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `frame` varchar(20) NOT NULL DEFAULT '', + `engine` varchar(20) NOT NULL DEFAULT '', + `server` mediumint(8) unsigned NOT NULL DEFAULT 0, + `pipeline` varchar(500) NOT NULL DEFAULT '', + `triggerType` varchar(255) NOT NULL DEFAULT '', + `sonarqubeServer` mediumint(8) unsigned NOT NULL DEFAULT 0, + `projectKey` varchar(255) NOT NULL DEFAULT '', + `svnDir` varchar(255) NOT NULL DEFAULT '', + `atDay` varchar(255) NOT NULL DEFAULT '', + `atTime` varchar(10) NOT NULL DEFAULT '', + `customParam` text DEFAULT NULL, + `comment` varchar(255) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `lastExec` datetime DEFAULT NULL, + `lastStatus` varchar(255) NOT NULL DEFAULT '', + `lastTag` varchar(255) NOT NULL DEFAULT '', + `lastSyncDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `idx_repo_deleted` (`repo`,`deleted`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanban` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `space` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `owner` varchar(30) NOT NULL DEFAULT '', + `team` text DEFAULT NULL, + `desc` mediumtext DEFAULT NULL, + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text DEFAULT NULL, + `archived` enum('0','1') NOT NULL DEFAULT '1', + `performable` enum('0','1') NOT NULL DEFAULT '0', + `status` enum('active','closed') NOT NULL DEFAULT 'active', + `order` mediumint(8) NOT NULL DEFAULT 0, + `displayCards` smallint(6) NOT NULL DEFAULT 0, + `showWIP` enum('0','1') NOT NULL DEFAULT '1', + `fluidBoard` enum('0','1') NOT NULL DEFAULT '0', + `colWidth` smallint(4) NOT NULL DEFAULT 264, + `minColWidth` smallint(4) NOT NULL DEFAULT 200, + `maxColWidth` smallint(4) NOT NULL DEFAULT 384, + `object` varchar(255) NOT NULL DEFAULT '', + `alignment` varchar(10) NOT NULL DEFAULT 'center', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `closedBy` char(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `activatedBy` char(30) NOT NULL DEFAULT '', + `activatedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbancard` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) unsigned NOT NULL DEFAULT 0, + `region` mediumint(8) unsigned NOT NULL DEFAULT 0, + `group` mediumint(8) unsigned NOT NULL DEFAULT 0, + `fromID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `fromType` varchar(30) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT 'doing', + `pri` mediumint(8) unsigned NOT NULL DEFAULT 0, + `assignedTo` text DEFAULT NULL, + `desc` mediumtext DEFAULT NULL, + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `estimate` float unsigned NOT NULL DEFAULT 0, + `progress` float unsigned NOT NULL DEFAULT 0, + `color` char(7) NOT NULL DEFAULT '', + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text DEFAULT NULL, + `order` mediumint(8) NOT NULL DEFAULT 0, + `archived` enum('0','1') NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `archivedBy` char(30) NOT NULL DEFAULT '', + `archivedDate` datetime DEFAULT NULL, + `assignedBy` char(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbancell` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) NOT NULL DEFAULT 0, + `lane` mediumint(8) NOT NULL DEFAULT 0, + `column` mediumint(8) NOT NULL DEFAULT 0, + `type` char(30) NOT NULL DEFAULT '', + `cards` mediumtext DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `card_group` (`kanban`,`type`,`lane`,`column`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbancolumn` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `parent` mediumint(8) NOT NULL DEFAULT 0, + `type` char(30) NOT NULL DEFAULT '', + `region` mediumint(8) unsigned NOT NULL DEFAULT 0, + `group` mediumint(8) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `color` char(30) NOT NULL DEFAULT '', + `limit` smallint(6) NOT NULL DEFAULT -1, + `order` mediumint(8) NOT NULL DEFAULT 0, + `archived` enum('0','1') NOT NULL DEFAULT '0', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbangroup` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `kanban` mediumint(8) unsigned NOT NULL DEFAULT 0, + `region` mediumint(8) unsigned NOT NULL DEFAULT 0, + `order` smallint(6) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbanlane` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `execution` mediumint(8) NOT NULL DEFAULT 0, + `type` char(30) NOT NULL DEFAULT '', + `region` mediumint(8) unsigned NOT NULL DEFAULT 0, + `group` mediumint(8) unsigned NOT NULL DEFAULT 0, + `groupby` char(30) NOT NULL DEFAULT '', + `extra` char(30) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `color` char(30) NOT NULL DEFAULT '', + `order` smallint(6) NOT NULL DEFAULT 0, + `lastEditedTime` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbanregion` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `space` mediumint(8) unsigned NOT NULL DEFAULT 0, + `kanban` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `order` mediumint(8) NOT NULL DEFAULT 0, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_kanbanspace` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `type` varchar(50) NOT NULL DEFAULT '', + `owner` varchar(30) NOT NULL DEFAULT '', + `team` text DEFAULT NULL, + `desc` mediumtext DEFAULT NULL, + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text DEFAULT NULL, + `status` enum('active','closed') NOT NULL DEFAULT 'active', + `order` mediumint(8) NOT NULL DEFAULT 0, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `closedBy` char(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `activatedBy` char(30) NOT NULL DEFAULT '', + `activatedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_lang` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `lang` varchar(30) NOT NULL DEFAULT '', + `module` varchar(30) NOT NULL DEFAULT '', + `section` varchar(50) NOT NULL DEFAULT '', + `key` varchar(60) NOT NULL DEFAULT '', + `value` text DEFAULT NULL, + `system` enum('0','1') NOT NULL DEFAULT '1', + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + PRIMARY KEY (`id`), + UNIQUE KEY `lang` (`lang`,`module`,`section`,`key`,`vision`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_leave` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `year` char(4) NOT NULL DEFAULT '', + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `start` time DEFAULT NULL, + `finish` time DEFAULT NULL, + `hours` float(4,1) unsigned NOT NULL DEFAULT 0.0, + `backDate` datetime DEFAULT NULL, + `type` varchar(30) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `status` varchar(30) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `reviewedBy` char(30) NOT NULL DEFAULT '', + `reviewedDate` datetime DEFAULT NULL, + `level` tinyint(3) NOT NULL DEFAULT 0, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `reviewers` text DEFAULT NULL, + `backReviewers` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `year` (`year`), + KEY `type` (`type`), + KEY `status` (`status`), + KEY `createdBy` (`createdBy`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_lieu` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `year` char(4) NOT NULL DEFAULT '', + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `start` time DEFAULT NULL, + `finish` time DEFAULT NULL, + `hours` float(4,1) unsigned NOT NULL DEFAULT 0.0, + `overtime` char(255) NOT NULL DEFAULT '', + `trip` char(255) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `status` varchar(30) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `reviewedBy` char(30) NOT NULL DEFAULT '', + `reviewedDate` datetime DEFAULT NULL, + `level` tinyint(3) NOT NULL DEFAULT 0, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `reviewers` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `year` (`year`), + KEY `status` (`status`), + KEY `createdBy` (`createdBy`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_log` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(30) NOT NULL DEFAULT '', + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `action` mediumint(8) unsigned NOT NULL DEFAULT 0, + `date` datetime DEFAULT NULL, + `url` varchar(255) NOT NULL DEFAULT '', + `contentType` varchar(30) NOT NULL DEFAULT '', + `data` text DEFAULT NULL, + `result` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `objectType` (`objectType`), + KEY `obejctID` (`objectID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_market` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `industry` char(255) NOT NULL DEFAULT '', + `scale` decimal(10,2) NOT NULL DEFAULT 0.00, + `maturity` char(255) NOT NULL DEFAULT '', + `speed` varchar(255) NOT NULL DEFAULT '', + `competition` char(255) NOT NULL DEFAULT '', + `strategy` varchar(255) NOT NULL DEFAULT '', + `ppm` varchar(20) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_marketreport` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `market` mediumint(8) NOT NULL DEFAULT 0, + `research` mediumint(8) NOT NULL DEFAULT 0, + `maturity` varchar(30) NOT NULL DEFAULT '', + `owner` varchar(30) NOT NULL DEFAULT '', + `participants` char(255) NOT NULL DEFAULT '', + `source` varchar(30) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `status` varchar(20) NOT NULL DEFAULT '', + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `publishedBy` varchar(30) NOT NULL DEFAULT '', + `publishedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_measqueue` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(30) NOT NULL DEFAULT '', + `mid` mediumint(8) unsigned NOT NULL DEFAULT 0, + `status` varchar(255) NOT NULL DEFAULT '', + `logs` text DEFAULT NULL, + `execTime` varchar(10) NOT NULL DEFAULT '', + `params` text DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `updateDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_measrecords` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `type` varchar(30) NOT NULL DEFAULT '', + `mid` mediumint(8) NOT NULL DEFAULT 0, + `measCode` char(50) NOT NULL DEFAULT '', + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `params` text DEFAULT NULL, + `year` char(4) NOT NULL DEFAULT '', + `month` char(6) NOT NULL DEFAULT '', + `week` char(8) NOT NULL DEFAULT '', + `day` char(8) NOT NULL DEFAULT '', + `value` varchar(255) NOT NULL DEFAULT '', + `date` date DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `time` (`year`,`month`,`day`,`week`), + KEY `product` (`product`), + KEY `project` (`project`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_meastemplate` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `model` char(30) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `content` mediumtext DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_meeting` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) NOT NULL DEFAULT 0, + `execution` mediumint(8) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `type` varchar(255) NOT NULL DEFAULT '', + `begin` time DEFAULT NULL, + `end` time DEFAULT NULL, + `dept` mediumint(8) NOT NULL DEFAULT 0, + `mode` varchar(255) NOT NULL DEFAULT '', + `host` varchar(30) NOT NULL DEFAULT '', + `participant` text DEFAULT NULL, + `date` date DEFAULT NULL, + `room` int(11) NOT NULL DEFAULT 0, + `minutes` text DEFAULT NULL, + `minutedBy` varchar(30) NOT NULL DEFAULT '', + `minutedDate` datetime DEFAULT NULL, + `objectType` varchar(30) NOT NULL DEFAULT '', + `objectID` mediumint(8) NOT NULL DEFAULT 0, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_meetingroom` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `position` varchar(30) NOT NULL DEFAULT '', + `seats` int(11) NOT NULL DEFAULT 0, + `equipment` varchar(255) NOT NULL DEFAULT '', + `openTime` varchar(255) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_metric` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `purpose` varchar(50) NOT NULL DEFAULT '', + `scope` char(30) NOT NULL DEFAULT '', + `object` char(30) NOT NULL DEFAULT '', + `stage` enum('wait','released') DEFAULT 'wait', + `type` enum('php','sql') DEFAULT 'php', + `name` varchar(90) NOT NULL DEFAULT '', + `alias` varchar(90) NOT NULL DEFAULT '', + `code` varchar(90) NOT NULL DEFAULT '', + `unit` varchar(10) NOT NULL DEFAULT '', + `dateType` varchar(50) NOT NULL DEFAULT '', + `collector` text DEFAULT NULL, + `desc` text DEFAULT NULL, + `definition` text DEFAULT NULL, + `when` varchar(30) NOT NULL DEFAULT '', + `event` varchar(30) NOT NULL DEFAULT '', + `cronCFG` varchar(30) NOT NULL DEFAULT '', + `time` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `implementedBy` varchar(30) NOT NULL DEFAULT '', + `implementedDate` datetime DEFAULT NULL, + `delistedBy` varchar(30) NOT NULL DEFAULT '', + `delistedDate` datetime DEFAULT NULL, + `builtin` enum('0','1') NOT NULL DEFAULT '0', + `fromID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `order` mediumint(8) unsigned NOT NULL DEFAULT 0, + `lastCalcRows` int(11) NOT NULL DEFAULT 0, + `lastCalcTime` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_metriclib` ( + `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + `metricID` mediumint(9) NOT NULL DEFAULT 0, + `metricCode` varchar(100) NOT NULL DEFAULT '', + `system` char(30) NOT NULL DEFAULT '0', + `program` char(30) NOT NULL DEFAULT '', + `project` char(30) NOT NULL DEFAULT '', + `product` char(30) NOT NULL DEFAULT '', + `execution` char(30) NOT NULL DEFAULT '', + `code` char(30) NOT NULL DEFAULT '', + `pipeline` char(30) NOT NULL DEFAULT '', + `repo` char(30) NOT NULL DEFAULT '', + `user` text DEFAULT NULL, + `dept` char(30) NOT NULL DEFAULT '', + `year` char(4) NOT NULL DEFAULT '0', + `month` char(2) NOT NULL DEFAULT '0', + `week` char(2) NOT NULL DEFAULT '0', + `day` char(2) NOT NULL DEFAULT '0', + `value` varchar(100) NOT NULL DEFAULT '0', + `calcType` enum('cron','inference') NOT NULL DEFAULT 'cron', + `calculatedBy` varchar(30) NOT NULL DEFAULT '', + `date` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `metricID` (`metricID`), + KEY `metricCode` (`metricCode`), + KEY `date` (`date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_module` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `root` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` char(60) NOT NULL DEFAULT '', + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `path` char(255) NOT NULL DEFAULT '', + `grade` tinyint(3) unsigned NOT NULL DEFAULT 0, + `order` smallint(5) unsigned NOT NULL DEFAULT 0, + `type` char(30) NOT NULL DEFAULT '', + `from` mediumint(8) unsigned NOT NULL DEFAULT 0, + `owner` varchar(30) NOT NULL DEFAULT '', + `collector` text DEFAULT NULL, + `short` varchar(30) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `root` (`root`), + KEY `type` (`type`), + KEY `path` (`path`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_mr` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `hostID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `sourceProject` varchar(50) NOT NULL DEFAULT '', + `sourceBranch` varchar(100) NOT NULL DEFAULT '', + `targetProject` varchar(50) NOT NULL DEFAULT '', + `targetBranch` varchar(100) NOT NULL DEFAULT '', + `mriid` int(10) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `description` text DEFAULT NULL, + `assignee` varchar(255) NOT NULL DEFAULT '', + `reviewer` varchar(255) NOT NULL DEFAULT '', + `approver` varchar(255) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `status` char(30) NOT NULL DEFAULT '', + `mergeStatus` char(30) NOT NULL DEFAULT '', + `approvalStatus` char(30) NOT NULL DEFAULT '', + `needApproved` enum('0','1') NOT NULL DEFAULT '0', + `needCI` enum('0','1') NOT NULL DEFAULT '0', + `repoID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `jobID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `executionID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `compileID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `compileStatus` char(30) NOT NULL DEFAULT '', + `removeSourceBranch` enum('0','1') NOT NULL DEFAULT '0', + `squash` enum('0','1') NOT NULL DEFAULT '0', + `synced` enum('0','1') NOT NULL DEFAULT '1', + `syncError` varchar(255) NOT NULL DEFAULT '', + `hasNoConflict` enum('0','1') NOT NULL DEFAULT '0', + `diffs` longtext DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_mrapproval` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `mrID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `account` varchar(255) NOT NULL DEFAULT '', + `date` datetime DEFAULT NULL, + `action` char(30) NOT NULL DEFAULT '', + `comment` text DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_nc` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `auditplan` mediumint(8) NOT NULL DEFAULT 0, + `listID` mediumint(8) NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `type` char(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT 'active', + `severity` char(30) NOT NULL DEFAULT '', + `deadline` date DEFAULT NULL, + `resolvedBy` varchar(30) NOT NULL DEFAULT '', + `resolution` char(30) NOT NULL DEFAULT '', + `resolvedDate` date DEFAULT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` date DEFAULT NULL, + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` date DEFAULT NULL, + `activateDate` date DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_notify` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `objectType` varchar(50) NOT NULL DEFAULT '', + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `action` mediumint(8) NOT NULL DEFAULT 0, + `toList` text DEFAULT NULL, + `ccList` text DEFAULT NULL, + `subject` text DEFAULT NULL, + `data` text DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `sendTime` datetime DEFAULT NULL, + `status` varchar(10) NOT NULL DEFAULT 'wait', + `failReason` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `objectType` (`objectType`), + KEY `status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_oauth` ( + `account` varchar(30) NOT NULL DEFAULT '', + `openID` varchar(255) NOT NULL DEFAULT '', + `providerType` varchar(30) NOT NULL DEFAULT '', + `providerID` mediumint(8) unsigned NOT NULL DEFAULT 0, + KEY `account` (`account`), + KEY `providerType` (`providerType`), + KEY `providerID` (`providerID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_object` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) NOT NULL DEFAULT 0, + `from` mediumint(8) NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `category` char(30) NOT NULL DEFAULT '', + `version` varchar(255) NOT NULL DEFAULT '', + `type` enum('reviewed','taged') NOT NULL DEFAULT 'reviewed', + `enabled` enum('0','1') NOT NULL DEFAULT '1', + `range` text DEFAULT NULL, + `data` text DEFAULT NULL, + `storyEst` char(30) NOT NULL DEFAULT '', + `taskEst` char(30) NOT NULL DEFAULT '', + `requestEst` char(30) NOT NULL DEFAULT '', + `testEst` char(30) NOT NULL DEFAULT '', + `devEst` char(30) NOT NULL DEFAULT '', + `designEst` char(30) NOT NULL DEFAULT '', + `end` date DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_opportunity` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `source` char(30) NOT NULL DEFAULT '', + `type` char(30) NOT NULL DEFAULT '', + `strategy` char(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT 'active', + `impact` mediumint(8) NOT NULL DEFAULT 0, + `chance` mediumint(8) NOT NULL DEFAULT 0, + `ratio` mediumint(8) NOT NULL DEFAULT 0, + `pri` char(30) NOT NULL DEFAULT '', + `identifiedDate` date DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` date DEFAULT NULL, + `approvedDate` date DEFAULT NULL, + `prevention` mediumtext DEFAULT NULL, + `plannedClosedDate` date DEFAULT NULL, + `actualClosedDate` date DEFAULT NULL, + `lib` mediumint(8) unsigned NOT NULL DEFAULT 0, + `from` mediumint(8) unsigned NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 1, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `activatedBy` varchar(30) NOT NULL DEFAULT '', + `activatedDate` datetime DEFAULT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `canceledBy` varchar(30) NOT NULL DEFAULT '', + `canceledDate` datetime DEFAULT NULL, + `cancelReason` char(30) NOT NULL DEFAULT '', + `hangupedBy` varchar(30) NOT NULL DEFAULT '', + `hangupedDate` datetime DEFAULT NULL, + `resolution` mediumtext DEFAULT NULL, + `resolvedBy` varchar(30) NOT NULL DEFAULT '', + `resolvedDate` datetime DEFAULT NULL, + `lastCheckedBy` varchar(30) NOT NULL DEFAULT '', + `lastCheckedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_overtime` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `year` char(4) NOT NULL DEFAULT '', + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `start` time DEFAULT NULL, + `finish` time DEFAULT NULL, + `hours` float(4,1) unsigned NOT NULL DEFAULT 0.0, + `leave` varchar(255) NOT NULL DEFAULT '', + `type` varchar(30) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `status` varchar(30) NOT NULL DEFAULT '', + `rejectReason` varchar(100) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `reviewedBy` char(30) NOT NULL DEFAULT '', + `reviewedDate` datetime DEFAULT NULL, + `level` tinyint(3) NOT NULL DEFAULT 0, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `reviewers` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `year` (`year`), + KEY `type` (`type`), + KEY `status` (`status`), + KEY `createdBy` (`createdBy`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_pipeline` ( + `id` smallint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` char(30) NOT NULL DEFAULT '', + `name` varchar(50) NOT NULL DEFAULT '', + `url` varchar(255) DEFAULT NULL, + `account` varchar(30) DEFAULT NULL, + `password` varchar(255) DEFAULT NULL, + `token` varchar(255) DEFAULT NULL, + `private` char(32) DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_pivot` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `dimension` mediumint(8) unsigned NOT NULL DEFAULT 0, + `group` varchar(255) NOT NULL DEFAULT '', + `code` varchar(255) NOT NULL DEFAULT '', + `driver` enum('mysql','duckdb') NOT NULL DEFAULT 'mysql', + `name` text DEFAULT NULL, + `desc` text DEFAULT NULL, + `sql` mediumtext DEFAULT NULL, + `fields` mediumtext DEFAULT NULL, + `langs` mediumtext DEFAULT NULL, + `vars` mediumtext DEFAULT NULL, + `objects` mediumtext DEFAULT NULL, + `settings` mediumtext DEFAULT NULL, + `filters` mediumtext DEFAULT NULL, + `step` tinyint(3) unsigned NOT NULL DEFAULT 0, + `stage` enum('draft','published') NOT NULL DEFAULT 'draft', + `builtin` enum('0','1') NOT NULL DEFAULT '0', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `dimension` (`dimension`), + KEY `group` (`group`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_pivotdrill` ( + `pivot` mediumint(9) NOT NULL, + `field` varchar(255) NOT NULL, + `object` varchar(40) NOT NULL, + `whereSql` mediumtext NOT NULL, + `condition` mediumtext NOT NULL, + `status` enum('design','published') NOT NULL DEFAULT 'published', + `account` varchar(30) NOT NULL DEFAULT '', + `type` enum('auto','manual') NOT NULL DEFAULT 'manual' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_planstory` ( + `plan` mediumint(8) unsigned NOT NULL DEFAULT 0, + `story` mediumint(8) unsigned NOT NULL DEFAULT 0, + `order` mediumint(9) NOT NULL DEFAULT 0, + UNIQUE KEY `plan_story` (`plan`,`story`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_practice` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` mediumint(8) unsigned NOT NULL DEFAULT 0, + `code` char(50) NOT NULL DEFAULT '', + `title` varchar(255) NOT NULL DEFAULT '', + `labels` varchar(255) NOT NULL DEFAULT '', + `summary` varchar(255) NOT NULL DEFAULT '', + `content` text DEFAULT NULL, + `contributor` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `code` (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_priv` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` varchar(30) NOT NULL DEFAULT '', + `method` varchar(30) NOT NULL DEFAULT '', + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `edition` varchar(30) NOT NULL DEFAULT ',open,biz,max,', + `vision` varchar(30) NOT NULL DEFAULT ',rnd,', + `system` enum('0','1') NOT NULL DEFAULT '0', + `order` mediumint(8) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `priv` (`module`,`method`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_privlang` ( + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `objectType` enum('priv','manager') NOT NULL DEFAULT 'priv', + `lang` varchar(30) NOT NULL DEFAULT '', + `key` varchar(100) NOT NULL DEFAULT '', + `value` varchar(255) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + UNIQUE KEY `objectlang` (`objectID`,`objectType`,`lang`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_privmanager` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `code` varchar(100) NOT NULL DEFAULT '', + `type` enum('view','module','package') NOT NULL DEFAULT 'package', + `edition` varchar(30) NOT NULL DEFAULT ',open,biz,max,', + `vision` varchar(30) NOT NULL DEFAULT ',rnd,', + `order` mediumint(8) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_privrelation` ( + `priv` varchar(100) NOT NULL DEFAULT '', + `type` varchar(30) NOT NULL DEFAULT '', + `relationPriv` varchar(100) NOT NULL DEFAULT '', + UNIQUE KEY `privrelation` (`priv`,`type`,`relationPriv`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_process` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `model` char(30) NOT NULL DEFAULT 'waterfall', + `name` varchar(255) NOT NULL DEFAULT '', + `type` char(30) NOT NULL DEFAULT '', + `abbr` char(30) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT '', + `order` mediumint(9) NOT NULL DEFAULT 0, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_product` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `program` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(110) NOT NULL DEFAULT '', + `code` varchar(45) NOT NULL DEFAULT '', + `shadow` tinyint(1) unsigned NOT NULL DEFAULT 0, + `bind` enum('0','1') NOT NULL DEFAULT '0', + `line` mediumint(8) NOT NULL DEFAULT 0, + `type` varchar(30) NOT NULL DEFAULT 'normal', + `status` varchar(30) NOT NULL DEFAULT '', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `PO` varchar(30) NOT NULL DEFAULT '', + `QD` varchar(30) NOT NULL DEFAULT '', + `RD` varchar(30) NOT NULL DEFAULT '', + `feedback` varchar(30) NOT NULL DEFAULT '', + `ticket` varchar(30) NOT NULL DEFAULT '', + `acl` enum('open','private','custom') NOT NULL DEFAULT 'open', + `groups` text DEFAULT NULL, + `whitelist` text DEFAULT NULL, + `reviewer` text DEFAULT NULL, + `PMT` text DEFAULT NULL, + `draftEpics` mediumint(8) NOT NULL DEFAULT 0, + `activeEpics` mediumint(8) NOT NULL DEFAULT 0, + `changingEpics` mediumint(8) NOT NULL DEFAULT 0, + `reviewingEpics` mediumint(8) NOT NULL DEFAULT 0, + `finishedEpics` mediumint(8) NOT NULL DEFAULT 0, + `closedEpics` mediumint(8) NOT NULL DEFAULT 0, + `totalEpics` mediumint(8) NOT NULL DEFAULT 0, + `draftRequirements` mediumint(8) NOT NULL DEFAULT 0, + `activeRequirements` mediumint(8) NOT NULL DEFAULT 0, + `changingRequirements` mediumint(8) NOT NULL DEFAULT 0, + `reviewingRequirements` mediumint(8) NOT NULL DEFAULT 0, + `finishedRequirements` mediumint(8) NOT NULL DEFAULT 0, + `closedRequirements` mediumint(8) NOT NULL DEFAULT 0, + `totalRequirements` mediumint(8) NOT NULL DEFAULT 0, + `draftStories` mediumint(8) NOT NULL DEFAULT 0, + `activeStories` mediumint(8) NOT NULL DEFAULT 0, + `changingStories` mediumint(8) NOT NULL DEFAULT 0, + `reviewingStories` mediumint(8) NOT NULL DEFAULT 0, + `finishedStories` mediumint(8) NOT NULL DEFAULT 0, + `closedStories` mediumint(8) NOT NULL DEFAULT 0, + `totalStories` mediumint(8) NOT NULL DEFAULT 0, + `unresolvedBugs` mediumint(8) NOT NULL DEFAULT 0, + `closedBugs` mediumint(8) NOT NULL DEFAULT 0, + `fixedBugs` mediumint(8) NOT NULL DEFAULT 0, + `totalBugs` mediumint(8) NOT NULL DEFAULT 0, + `plans` mediumint(8) NOT NULL DEFAULT 0, + `releases` mediumint(8) NOT NULL DEFAULT 0, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `createdVersion` varchar(20) NOT NULL DEFAULT '', + `closedDate` date DEFAULT NULL, + `order` mediumint(8) unsigned NOT NULL DEFAULT 0, + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `acl` (`acl`), + KEY `order` (`order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_productplan` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` varchar(255) NOT NULL DEFAULT '0', + `parent` mediumint(9) NOT NULL DEFAULT 0, + `title` varchar(90) NOT NULL DEFAULT '', + `status` enum('wait','doing','done','closed') NOT NULL DEFAULT 'wait', + `desc` mediumtext DEFAULT NULL, + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `finishedDate` datetime DEFAULT NULL, + `closedDate` datetime DEFAULT NULL, + `order` text DEFAULT NULL, + `closedReason` varchar(20) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `end` (`end`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_programactivity` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `process` mediumint(8) NOT NULL DEFAULT 0, + `activity` mediumint(8) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `content` text DEFAULT NULL, + `reason` varchar(255) NOT NULL DEFAULT '', + `result` char(30) NOT NULL DEFAULT '', + `linkedBy` char(30) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_programoutput` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `process` mediumint(8) NOT NULL DEFAULT 0, + `activity` mediumint(8) NOT NULL DEFAULT 0, + `output` mediumint(8) NOT NULL DEFAULT 0, + `content` text DEFAULT NULL, + `name` varchar(255) NOT NULL DEFAULT '', + `reason` varchar(255) NOT NULL DEFAULT '', + `result` char(30) NOT NULL DEFAULT '', + `linkedBy` char(30) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_programprocess` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `process` mediumint(8) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `type` char(30) NOT NULL DEFAULT '', + `abbr` char(30) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `reason` varchar(255) NOT NULL DEFAULT '', + `linkedBy` char(30) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_programreport` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `template` mediumint(8) NOT NULL DEFAULT 0, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `params` text DEFAULT NULL, + `content` text DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_project` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) NOT NULL DEFAULT 0, + `charter` mediumint(8) NOT NULL DEFAULT 0, + `model` char(30) NOT NULL DEFAULT '', + `type` char(30) NOT NULL DEFAULT 'sprint', + `category` char(30) NOT NULL DEFAULT '', + `lifetime` char(30) NOT NULL DEFAULT '', + `budget` varchar(30) NOT NULL DEFAULT '0', + `budgetUnit` char(30) NOT NULL DEFAULT 'CNY', + `attribute` varchar(30) NOT NULL DEFAULT '', + `percent` float unsigned NOT NULL DEFAULT 0, + `milestone` enum('0','1') NOT NULL DEFAULT '0', + `output` text DEFAULT NULL, + `auth` char(30) NOT NULL DEFAULT '', + `storyType` char(30) NOT NULL DEFAULT '', + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `path` varchar(255) NOT NULL DEFAULT '', + `grade` tinyint(3) unsigned NOT NULL DEFAULT 0, + `name` varchar(90) NOT NULL DEFAULT '', + `code` varchar(45) NOT NULL DEFAULT '', + `hasProduct` tinyint(1) unsigned NOT NULL DEFAULT 1, + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `firstEnd` date DEFAULT NULL, + `realBegan` date DEFAULT NULL, + `realEnd` date DEFAULT NULL, + `days` smallint(6) unsigned NOT NULL DEFAULT 0, + `status` varchar(10) NOT NULL DEFAULT '', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `pri` enum('1','2','3','4') NOT NULL DEFAULT '1', + `desc` mediumtext DEFAULT NULL, + `version` smallint(6) NOT NULL DEFAULT 0, + `parentVersion` smallint(6) NOT NULL DEFAULT 0, + `planDuration` int(11) NOT NULL DEFAULT 0, + `realDuration` int(11) NOT NULL DEFAULT 0, + `progress` decimal(5,2) NOT NULL DEFAULT 0.00, + `estimate` float NOT NULL DEFAULT 0, + `left` float NOT NULL DEFAULT 0, + `consumed` float NOT NULL DEFAULT 0, + `teamCount` int(11) NOT NULL DEFAULT 0, + `market` mediumint(8) NOT NULL DEFAULT 0, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + `openedVersion` varchar(20) NOT NULL DEFAULT '', + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `closedReason` varchar(20) NOT NULL DEFAULT '', + `canceledBy` varchar(30) NOT NULL DEFAULT '', + `canceledDate` datetime DEFAULT NULL, + `suspendedDate` date DEFAULT NULL, + `PO` varchar(30) NOT NULL DEFAULT '', + `PM` varchar(30) NOT NULL DEFAULT '', + `QD` varchar(30) NOT NULL DEFAULT '', + `RD` varchar(30) NOT NULL DEFAULT '', + `team` varchar(90) NOT NULL DEFAULT '', + `acl` char(30) NOT NULL DEFAULT 'open', + `whitelist` text DEFAULT NULL, + `order` mediumint(8) unsigned NOT NULL DEFAULT 0, + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `stageBy` enum('project','product') NOT NULL DEFAULT 'product', + `displayCards` smallint(6) NOT NULL DEFAULT 0, + `fluidBoard` enum('0','1') NOT NULL DEFAULT '0', + `multiple` enum('0','1') NOT NULL DEFAULT '1', + `parallel` mediumint(9) NOT NULL DEFAULT 0, + `enabled` enum('on','off') NOT NULL DEFAULT 'on', + `colWidth` smallint(6) NOT NULL DEFAULT 264, + `minColWidth` smallint(6) NOT NULL DEFAULT 200, + `maxColWidth` smallint(6) NOT NULL DEFAULT 384, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `parent` (`parent`), + KEY `begin` (`begin`), + KEY `end` (`end`), + KEY `status` (`status`), + KEY `acl` (`acl`), + KEY `order` (`order`), + KEY `project` (`project`), + KEY `type_order` (`type`,`order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectadmin` ( + `group` smallint(6) NOT NULL DEFAULT 0, + `account` char(30) NOT NULL DEFAULT '', + `programs` text DEFAULT NULL, + `projects` text DEFAULT NULL, + `products` text DEFAULT NULL, + `executions` text DEFAULT NULL, + UNIQUE KEY `group_account` (`group`,`account`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectcase` ( + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `case` mediumint(8) unsigned NOT NULL DEFAULT 0, + `count` mediumint(8) unsigned NOT NULL DEFAULT 1, + `version` smallint(6) NOT NULL DEFAULT 1, + `order` smallint(6) unsigned NOT NULL DEFAULT 0, + UNIQUE KEY `project` (`project`,`case`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectproduct` ( + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` mediumint(8) unsigned NOT NULL DEFAULT 0, + `plan` varchar(255) NOT NULL DEFAULT '', + `roadmap` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`project`,`product`,`branch`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectspec` ( + `project` mediumint(8) NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `milestone` enum('0','1') NOT NULL DEFAULT '0', + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + UNIQUE KEY `project` (`project`,`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_projectstory` ( + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` mediumint(8) unsigned NOT NULL DEFAULT 0, + `story` mediumint(8) unsigned NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 1, + `order` smallint(6) unsigned NOT NULL DEFAULT 0, + UNIQUE KEY `project` (`project`,`story`), + KEY `story` (`story`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_queue` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `cron` mediumint(9) NOT NULL, + `type` varchar(255) NOT NULL, + `command` text NOT NULL, + `status` enum('wait','doing','done') NOT NULL DEFAULT 'wait', + `execId` int(11) DEFAULT NULL, + `createdDate` datetime NOT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_relation` ( + `id` int(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) NOT NULL DEFAULT 0, + `product` mediumint(8) NOT NULL DEFAULT 0, + `execution` mediumint(8) NOT NULL DEFAULT 0, + `AType` char(30) NOT NULL DEFAULT '', + `AID` mediumint(8) NOT NULL DEFAULT 0, + `AVersion` char(30) NOT NULL DEFAULT '', + `relation` char(30) NOT NULL DEFAULT '', + `BType` char(30) NOT NULL DEFAULT '', + `BID` mediumint(8) NOT NULL DEFAULT 0, + `BVersion` char(30) NOT NULL DEFAULT '', + `extra` char(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `relation` (`product`,`relation`,`AType`,`BType`,`AID`,`BID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_relationoftasks` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `execution` mediumint(8) unsigned NOT NULL, + `pretask` mediumint(8) unsigned NOT NULL, + `condition` enum('begin','end') NOT NULL, + `task` mediumint(8) unsigned NOT NULL, + `action` enum('begin','end') NOT NULL, + PRIMARY KEY (`id`), + KEY `relationoftasks` (`execution`,`task`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_release` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` varchar(255) NOT NULL DEFAULT '0', + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` varchar(255) NOT NULL DEFAULT '0', + `shadow` mediumint(8) unsigned NOT NULL DEFAULT 0, + `build` varchar(255) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `marker` enum('0','1') NOT NULL DEFAULT '0', + `date` date DEFAULT NULL, + `releasedDate` date DEFAULT NULL, + `stories` text DEFAULT NULL, + `bugs` text DEFAULT NULL, + `leftBugs` text DEFAULT NULL, + `desc` mediumtext DEFAULT NULL, + `mailto` text DEFAULT NULL, + `notify` varchar(255) NOT NULL DEFAULT '', + `status` varchar(20) NOT NULL DEFAULT 'normal', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `build` (`build`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repo` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `product` varchar(255) NOT NULL DEFAULT '', + `projects` varchar(255) NOT NULL DEFAULT '', + `name` varchar(255) NOT NULL DEFAULT '', + `path` varchar(255) NOT NULL DEFAULT '', + `prefix` varchar(100) NOT NULL DEFAULT '', + `encoding` varchar(20) NOT NULL DEFAULT '', + `SCM` varchar(10) NOT NULL DEFAULT '', + `client` varchar(100) NOT NULL DEFAULT '', + `serviceHost` varchar(50) NOT NULL DEFAULT '', + `serviceProject` varchar(100) NOT NULL DEFAULT '', + `commits` mediumint(8) unsigned NOT NULL DEFAULT 0, + `account` varchar(30) NOT NULL DEFAULT '', + `password` varchar(30) NOT NULL DEFAULT '', + `encrypt` varchar(30) NOT NULL DEFAULT 'plain', + `acl` text DEFAULT NULL, + `synced` tinyint(1) NOT NULL DEFAULT 0, + `lastSync` datetime DEFAULT NULL, + `lastCommit` datetime DEFAULT NULL, + `desc` text DEFAULT NULL, + `extra` char(30) NOT NULL DEFAULT '', + `preMerge` enum('0','1') NOT NULL DEFAULT '0', + `job` mediumint(8) unsigned NOT NULL DEFAULT 0, + `fileServerUrl` text DEFAULT NULL, + `fileServerAccount` varchar(40) NOT NULL DEFAULT '', + `fileServerPassword` varchar(100) NOT NULL DEFAULT '', + `deleted` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repobranch` ( + `repo` mediumint(8) unsigned NOT NULL DEFAULT 0, + `revision` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` varchar(255) NOT NULL DEFAULT '', + UNIQUE KEY `repo_revision_branch` (`repo`,`revision`,`branch`), + KEY `branch` (`branch`), + KEY `revision` (`revision`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repofiles` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `repo` mediumint(8) unsigned NOT NULL DEFAULT 0, + `revision` mediumint(8) unsigned NOT NULL DEFAULT 0, + `path` varchar(255) NOT NULL DEFAULT '', + `oldPath` varchar(255) NOT NULL DEFAULT '', + `parent` varchar(255) NOT NULL DEFAULT '', + `type` varchar(20) NOT NULL DEFAULT '', + `action` char(1) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + KEY `path` (`path`), + KEY `parent` (`parent`), + KEY `repo` (`repo`), + KEY `revision` (`revision`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_repohistory` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `repo` mediumint(9) NOT NULL DEFAULT 0, + `revision` varchar(40) NOT NULL DEFAULT '', + `commit` mediumint(8) unsigned NOT NULL DEFAULT 0, + `comment` text DEFAULT NULL, + `committer` varchar(100) NOT NULL DEFAULT '', + `time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `repo` (`repo`), + KEY `revision` (`revision`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_report` ( + `id` mediumint(9) NOT NULL AUTO_INCREMENT, + `code` varchar(100) NOT NULL DEFAULT '', + `name` text DEFAULT NULL, + `dimension` int(8) NOT NULL DEFAULT 0, + `module` varchar(100) NOT NULL DEFAULT '', + `sql` text DEFAULT NULL, + `vars` text DEFAULT NULL, + `langs` text DEFAULT NULL, + `params` text DEFAULT NULL, + `step` tinyint(1) NOT NULL DEFAULT 2, + `desc` text DEFAULT NULL, + `addedBy` char(30) NOT NULL DEFAULT '', + `addedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `code` (`code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_researchplan` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `customer` varchar(255) NOT NULL DEFAULT '', + `stakeholder` varchar(255) NOT NULL DEFAULT '', + `objective` varchar(255) NOT NULL DEFAULT '', + `begin` datetime DEFAULT NULL, + `end` datetime DEFAULT NULL, + `location` varchar(255) NOT NULL DEFAULT '', + `team` varchar(255) NOT NULL DEFAULT '', + `method` enum('','videoConference','interview','questionnaire','telephoneInterview') NOT NULL DEFAULT '', + `outline` mediumtext DEFAULT NULL, + `schedule` mediumtext DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_researchreport` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `relatedPlan` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `author` varchar(30) NOT NULL DEFAULT '', + `content` mediumtext DEFAULT NULL, + `customer` varchar(255) NOT NULL DEFAULT '', + `researchObjects` varchar(255) NOT NULL DEFAULT '', + `begin` datetime DEFAULT NULL, + `end` datetime DEFAULT NULL, + `location` varchar(255) NOT NULL DEFAULT '', + `method` enum('','videoConference','interview','questionnaire','telephoneInterview') NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_review` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `object` mediumint(8) NOT NULL DEFAULT 0, + `template` mediumint(8) NOT NULL DEFAULT 0, + `doc` varchar(255) NOT NULL DEFAULT '', + `docVersion` varchar(255) NOT NULL DEFAULT '', + `status` char(30) NOT NULL DEFAULT '', + `reviewedBy` varchar(255) NOT NULL DEFAULT '', + `auditedBy` varchar(255) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `begin` date DEFAULT NULL, + `deadline` date DEFAULT NULL, + `lastReviewedBy` varchar(255) NOT NULL DEFAULT '', + `lastReviewedDate` date DEFAULT NULL, + `lastAuditedBy` varchar(255) NOT NULL DEFAULT '', + `lastAuditedDate` date DEFAULT NULL, + `lastEditedBy` varchar(255) NOT NULL DEFAULT '', + `lastEditedDate` date DEFAULT NULL, + `result` char(30) NOT NULL DEFAULT '', + `auditResult` char(30) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_reviewcl` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL DEFAULT '', + `object` char(30) NOT NULL DEFAULT '', + `category` char(30) NOT NULL DEFAULT '', + `type` varchar(255) NOT NULL DEFAULT '', + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `order` mediumint(8) DEFAULT 0, + `status` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_reviewissue` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `review` mediumint(8) NOT NULL DEFAULT 0, + `approval` mediumint(8) NOT NULL DEFAULT 0, + `injection` mediumint(8) NOT NULL DEFAULT 0, + `identify` mediumint(8) NOT NULL DEFAULT 0, + `type` char(30) NOT NULL DEFAULT 'review', + `listID` mediumint(8) NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `opinion` mediumtext DEFAULT NULL, + `opinionDate` date DEFAULT NULL, + `status` char(30) NOT NULL DEFAULT '', + `resolution` char(30) NOT NULL DEFAULT '', + `resolutionBy` char(30) NOT NULL DEFAULT '', + `resolutionDate` date DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_reviewlist` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `title` varchar(255) NOT NULL DEFAULT '', + `object` char(30) NOT NULL DEFAULT '', + `category` char(30) NOT NULL DEFAULT '', + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_reviewresult` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `review` mediumint(8) NOT NULL DEFAULT 0, + `type` char(30) NOT NULL DEFAULT 'review', + `result` char(30) NOT NULL DEFAULT '', + `opinion` text DEFAULT NULL, + `reviewer` char(30) NOT NULL DEFAULT '', + `remainIssue` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `consumed` float NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `reviewer` (`review`,`reviewer`,`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_risk` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` varchar(255) NOT NULL DEFAULT '', + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `source` char(30) NOT NULL DEFAULT '', + `category` char(30) NOT NULL DEFAULT '', + `strategy` char(30) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT 'active', + `impact` char(30) NOT NULL DEFAULT '', + `probability` char(30) NOT NULL DEFAULT '', + `rate` char(30) NOT NULL DEFAULT '', + `pri` char(30) NOT NULL DEFAULT '', + `identifiedDate` date DEFAULT NULL, + `prevention` mediumtext DEFAULT NULL, + `remedy` mediumtext DEFAULT NULL, + `plannedClosedDate` date DEFAULT NULL, + `actualClosedDate` date DEFAULT NULL, + `lib` mediumint(8) unsigned NOT NULL DEFAULT 0, + `from` mediumint(8) unsigned NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 1, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `resolution` mediumtext DEFAULT NULL, + `resolvedBy` varchar(30) NOT NULL DEFAULT '', + `activateBy` varchar(30) NOT NULL DEFAULT '', + `activateDate` date DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` date DEFAULT NULL, + `cancelBy` varchar(30) NOT NULL DEFAULT '', + `cancelDate` date DEFAULT NULL, + `cancelReason` char(30) NOT NULL DEFAULT '', + `hangupBy` varchar(30) NOT NULL DEFAULT '', + `hangupDate` date DEFAULT NULL, + `trackedBy` varchar(30) NOT NULL DEFAULT '', + `trackedDate` date DEFAULT NULL, + `assignedDate` date DEFAULT NULL, + `approvedDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_riskissue` ( + `risk` mediumint(8) unsigned NOT NULL DEFAULT 0, + `issue` mediumint(8) unsigned NOT NULL DEFAULT 0, + UNIQUE KEY `risk_issue` (`risk`,`issue`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_roadmap` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `product` mediumint(8) NOT NULL DEFAULT 0, + `branch` mediumint(8) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `status` char(30) NOT NULL DEFAULT '', + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `desc` longtext DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `closedBy` char(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `closedReason` enum('done','canceled') DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_roadmapstory` ( + `roadmap` mediumint(8) unsigned NOT NULL DEFAULT 0, + `story` mediumint(8) unsigned NOT NULL DEFAULT 0, + `order` mediumint(8) unsigned NOT NULL, + UNIQUE KEY `roadmap_story` (`roadmap`,`story`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_scene` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` mediumint(8) unsigned NOT NULL DEFAULT 0, + `module` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `sort` int(11) unsigned NOT NULL DEFAULT 0, + `openedBy` char(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `parent` int(11) NOT NULL DEFAULT 0, + `grade` tinyint(3) NOT NULL DEFAULT 0, + `path` varchar(1000) NOT NULL DEFAULT '', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_score` ( + `id` bigint(12) unsigned NOT NULL AUTO_INCREMENT, + `account` varchar(30) NOT NULL DEFAULT '', + `module` varchar(30) NOT NULL DEFAULT '', + `method` varchar(30) NOT NULL DEFAULT '', + `desc` varchar(250) NOT NULL DEFAULT '', + `before` int(11) NOT NULL DEFAULT 0, + `score` int(11) NOT NULL DEFAULT 0, + `after` int(11) NOT NULL DEFAULT 0, + `time` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `account` (`account`), + KEY `module` (`module`), + KEY `method` (`method`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_screen` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `dimension` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `cover` mediumtext DEFAULT NULL, + `scheme` mediumtext DEFAULT NULL, + `status` enum('draft','published') NOT NULL DEFAULT 'draft', + `builtin` enum('0','1') NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` char(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_searchdict` ( + `key` smallint(6) unsigned NOT NULL DEFAULT 0, + `value` char(3) NOT NULL DEFAULT '', + PRIMARY KEY (`key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_searchindex` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `objectType` char(20) NOT NULL DEFAULT '', + `objectID` mediumint(9) NOT NULL DEFAULT 0, + `title` text DEFAULT NULL, + `content` text DEFAULT NULL, + `addedDate` datetime DEFAULT NULL, + `editedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `object` (`objectType`,`objectID`), + KEY `addedDate` (`addedDate`), + FULLTEXT KEY `title_content` (`title`,`content`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_serverroom` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(128) NOT NULL DEFAULT '', + `city` varchar(128) NOT NULL DEFAULT '', + `line` varchar(20) NOT NULL DEFAULT '', + `bandwidth` varchar(128) NOT NULL DEFAULT '', + `provider` varchar(128) NOT NULL DEFAULT '', + `owner` varchar(30) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` char(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_service` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `external` enum('0','1') NOT NULL DEFAULT '0', + `port` smallint(5) unsigned NOT NULL DEFAULT 0, + `entry` varchar(255) NOT NULL DEFAULT '', + `deploy` varchar(255) NOT NULL DEFAULT '', + `version` varchar(64) NOT NULL DEFAULT '', + `color` char(7) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `dept` varchar(128) NOT NULL DEFAULT '', + `devel` varchar(30) NOT NULL DEFAULT '', + `qa` varchar(30) NOT NULL DEFAULT '', + `ops` varchar(30) NOT NULL DEFAULT '', + `hosts` text DEFAULT NULL, + `softName` varchar(128) NOT NULL DEFAULT '', + `softVersion` varchar(128) NOT NULL DEFAULT '', + `type` varchar(20) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` char(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `path` char(255) NOT NULL DEFAULT '', + `grade` tinyint(3) unsigned NOT NULL DEFAULT 0, + `order` smallint(5) unsigned NOT NULL DEFAULT 0, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_session` ( + `id` varchar(32) NOT NULL, + `data` mediumtext DEFAULT NULL, + `timestamp` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `timestamp` (`timestamp`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_solution` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` char(50) NOT NULL DEFAULT '', + `appID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `appName` char(50) NOT NULL DEFAULT '', + `appVersion` char(20) NOT NULL DEFAULT '', + `version` char(50) NOT NULL DEFAULT '', + `chart` char(50) NOT NULL DEFAULT '', + `cover` varchar(255) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `introduction` varchar(255) NOT NULL DEFAULT '', + `source` char(20) NOT NULL DEFAULT '', + `channel` char(20) NOT NULL DEFAULT '', + `components` text DEFAULT NULL, + `status` char(20) NOT NULL DEFAULT '', + `deleted` tinyint(1) NOT NULL DEFAULT 0, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdAt` datetime DEFAULT NULL, + `updatedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_solutions` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `contents` text NOT NULL, + `support` text NOT NULL, + `measures` text NOT NULL, + `type` char(30) NOT NULL DEFAULT '', + `addedBy` varchar(30) NOT NULL DEFAULT '', + `addedDate` date DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_space` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(200) NOT NULL, + `k8space` char(64) NOT NULL, + `owner` char(30) NOT NULL, + `default` tinyint(1) NOT NULL DEFAULT 0, + `createdAt` datetime DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_sqlview` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(90) NOT NULL DEFAULT '', + `code` varchar(45) NOT NULL DEFAULT '', + `sql` text DEFAULT NULL, + `desc` text DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_stage` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` varchar(255) NOT NULL DEFAULT '', + `percent` varchar(255) NOT NULL DEFAULT '', + `type` varchar(255) NOT NULL DEFAULT '', + `projectType` varchar(255) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_stakeholder` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `objectID` mediumint(8) NOT NULL DEFAULT 0, + `objectType` char(30) NOT NULL DEFAULT '', + `user` char(30) NOT NULL DEFAULT '', + `type` char(30) NOT NULL DEFAULT '', + `key` enum('0','1') NOT NULL DEFAULT '0', + `from` char(30) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `editedBy` char(30) NOT NULL DEFAULT '', + `editedDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `objectID` (`objectID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_story` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `parent` mediumint(9) NOT NULL DEFAULT 0, + `isParent` enum('0','1') NOT NULL DEFAULT '0', + `root` mediumint(9) NOT NULL DEFAULT 0, + `path` text DEFAULT NULL, + `grade` smallint(6) NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` mediumint(8) unsigned NOT NULL DEFAULT 0, + `module` mediumint(8) unsigned NOT NULL DEFAULT 0, + `plan` text DEFAULT NULL, + `source` varchar(20) NOT NULL DEFAULT '', + `sourceNote` varchar(255) NOT NULL DEFAULT '', + `fromBug` mediumint(8) unsigned NOT NULL DEFAULT 0, + `feedback` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `keywords` varchar(255) NOT NULL DEFAULT '', + `type` varchar(30) NOT NULL DEFAULT 'story', + `category` varchar(30) NOT NULL DEFAULT 'feature', + `pri` tinyint(3) unsigned NOT NULL DEFAULT 3, + `estimate` float unsigned NOT NULL DEFAULT 0, + `status` enum('','changing','active','draft','closed','reviewing','launched','developing') NOT NULL DEFAULT '', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL DEFAULT '', + `stage` enum('','wait','inroadmap','incharter','planned','projected','designing','designed','developing','developed','testing','tested','verified','rejected','delivering','delivered','released','closed') NOT NULL DEFAULT 'wait', + `stagedBy` char(30) NOT NULL DEFAULT '', + `mailto` text DEFAULT NULL, + `lib` mediumint(8) unsigned NOT NULL DEFAULT 0, + `fromStory` mediumint(8) unsigned NOT NULL DEFAULT 0, + `fromVersion` smallint(6) NOT NULL DEFAULT 1, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `approvedDate` date DEFAULT NULL, + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `changedBy` varchar(30) NOT NULL DEFAULT '', + `changedDate` datetime DEFAULT NULL, + `reviewedBy` varchar(255) NOT NULL DEFAULT '', + `reviewedDate` datetime DEFAULT NULL, + `releasedDate` datetime DEFAULT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `closedReason` varchar(30) NOT NULL DEFAULT '', + `activatedDate` datetime DEFAULT NULL, + `toBug` mediumint(8) unsigned NOT NULL DEFAULT 0, + `linkStories` varchar(255) NOT NULL DEFAULT '', + `linkRequirements` varchar(255) NOT NULL DEFAULT '', + `twins` varchar(255) NOT NULL DEFAULT '', + `duplicateStory` mediumint(8) unsigned NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 1, + `parentVersion` smallint(6) NOT NULL DEFAULT 0, + `demandVersion` smallint(6) NOT NULL DEFAULT 0, + `storyChanged` enum('0','1') NOT NULL DEFAULT '0', + `feedbackBy` varchar(100) NOT NULL DEFAULT '', + `notifyEmail` varchar(100) NOT NULL DEFAULT '', + `BSA` char(30) NOT NULL DEFAULT '', + `duration` char(30) NOT NULL DEFAULT '', + `demand` mediumint(8) NOT NULL DEFAULT 0, + `submitedBy` varchar(30) NOT NULL DEFAULT '', + `roadmap` varchar(255) NOT NULL DEFAULT '', + `URChanged` enum('0','1') NOT NULL DEFAULT '0', + `unlinkReason` enum('','omit','other') NOT NULL DEFAULT '', + `retractedReason` enum('','omit','other') NOT NULL DEFAULT '', + `retractedBy` varchar(30) NOT NULL DEFAULT '', + `retractedDate` datetime DEFAULT NULL, + `verifiedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `root` (`root`), + KEY `status` (`status`), + KEY `assignedTo` (`assignedTo`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storyestimate` ( + `story` mediumint(9) NOT NULL DEFAULT 0, + `round` smallint(6) NOT NULL DEFAULT 0, + `estimate` text DEFAULT NULL, + `average` float NOT NULL DEFAULT 0, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + UNIQUE KEY `story` (`story`,`round`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storygrade` ( + `type` enum('story','requirement','epic') NOT NULL, + `grade` smallint(6) NOT NULL, + `name` char(30) NOT NULL, + `status` char(30) NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storyreview` ( + `story` mediumint(9) NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 0, + `reviewer` varchar(30) NOT NULL DEFAULT '', + `result` varchar(30) NOT NULL DEFAULT '', + `reviewDate` datetime DEFAULT NULL, + UNIQUE KEY `story` (`story`,`version`,`reviewer`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storyspec` ( + `story` mediumint(9) NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `spec` mediumtext DEFAULT NULL, + `verify` mediumtext DEFAULT NULL, + `files` text DEFAULT NULL, + UNIQUE KEY `story` (`story`,`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_storystage` ( + `story` mediumint(8) unsigned NOT NULL DEFAULT 0, + `branch` mediumint(8) unsigned NOT NULL DEFAULT 0, + `stage` varchar(50) NOT NULL DEFAULT '', + `stagedBy` char(30) NOT NULL DEFAULT '', + UNIQUE KEY `story_branch` (`story`,`branch`), + KEY `story` (`story`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_suitecase` ( + `suite` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `case` mediumint(8) unsigned NOT NULL DEFAULT 0, + `version` smallint(5) unsigned NOT NULL DEFAULT 0, + UNIQUE KEY `suitecase` (`suite`,`case`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_task` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `parent` mediumint(8) NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `module` mediumint(8) unsigned NOT NULL DEFAULT 0, + `design` mediumint(8) unsigned NOT NULL DEFAULT 0, + `story` mediumint(8) unsigned NOT NULL DEFAULT 0, + `storyVersion` smallint(6) NOT NULL DEFAULT 1, + `designVersion` smallint(6) unsigned NOT NULL DEFAULT 1, + `fromBug` mediumint(8) unsigned NOT NULL DEFAULT 0, + `feedback` mediumint(8) unsigned NOT NULL DEFAULT 0, + `fromIssue` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `type` varchar(20) NOT NULL DEFAULT '', + `mode` varchar(10) NOT NULL DEFAULT '', + `pri` tinyint(3) unsigned NOT NULL DEFAULT 0, + `estimate` float unsigned NOT NULL DEFAULT 0, + `consumed` float unsigned NOT NULL DEFAULT 0, + `left` float unsigned NOT NULL DEFAULT 0, + `deadline` date DEFAULT NULL, + `status` enum('wait','doing','done','pause','cancel','closed') NOT NULL DEFAULT 'wait', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `color` char(7) NOT NULL DEFAULT '', + `mailto` text DEFAULT NULL, + `keywords` varchar(255) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `version` smallint(6) NOT NULL DEFAULT 0, + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `estStarted` date DEFAULT NULL, + `realStarted` datetime DEFAULT NULL, + `finishedBy` varchar(30) NOT NULL DEFAULT '', + `finishedDate` datetime DEFAULT NULL, + `finishedList` text DEFAULT NULL, + `canceledBy` varchar(30) NOT NULL DEFAULT '', + `canceledDate` datetime DEFAULT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `planDuration` int(11) NOT NULL DEFAULT 0, + `realDuration` int(11) NOT NULL DEFAULT 0, + `closedReason` varchar(30) NOT NULL DEFAULT '', + `lastEditedBy` varchar(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `activatedDate` datetime DEFAULT NULL, + `order` mediumint(8) unsigned NOT NULL DEFAULT 0, + `repo` mediumint(8) unsigned NOT NULL DEFAULT 0, + `mr` mediumint(8) unsigned NOT NULL DEFAULT 0, + `entry` varchar(255) NOT NULL DEFAULT '', + `lines` varchar(10) NOT NULL DEFAULT '', + `v1` varchar(40) NOT NULL DEFAULT '', + `v2` varchar(40) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + PRIMARY KEY (`id`), + KEY `execution` (`execution`), + KEY `story` (`story`), + KEY `parent` (`parent`), + KEY `assignedTo` (`assignedTo`), + KEY `order` (`order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_taskestimate` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `task` mediumint(8) unsigned NOT NULL DEFAULT 0, + `date` date DEFAULT NULL, + `left` float unsigned NOT NULL DEFAULT 0, + `consumed` float unsigned NOT NULL DEFAULT 0, + `account` char(30) NOT NULL DEFAULT '', + `work` text DEFAULT NULL, + `order` tinyint(3) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `task` (`task`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_taskspec` ( + `task` mediumint(8) NOT NULL DEFAULT 0, + `version` smallint(6) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `estStarted` date DEFAULT NULL, + `deadline` date DEFAULT NULL, + UNIQUE KEY `task` (`task`,`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_taskteam` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `task` mediumint(8) unsigned NOT NULL DEFAULT 0, + `account` char(30) NOT NULL DEFAULT '', + `estimate` decimal(12,2) NOT NULL DEFAULT 0.00, + `consumed` decimal(12,2) NOT NULL DEFAULT 0.00, + `left` decimal(12,2) NOT NULL DEFAULT 0.00, + `transfer` char(30) NOT NULL DEFAULT '', + `status` enum('wait','doing','done') NOT NULL DEFAULT 'wait', + `order` tinyint(3) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `task` (`task`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_team` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `root` mediumint(8) unsigned NOT NULL DEFAULT 0, + `type` enum('project','task','execution') NOT NULL DEFAULT 'project', + `account` char(30) NOT NULL DEFAULT '', + `role` char(30) NOT NULL DEFAULT '', + `position` varchar(30) NOT NULL DEFAULT '', + `limited` char(8) NOT NULL DEFAULT 'no', + `join` date DEFAULT NULL, + `days` smallint(5) unsigned NOT NULL DEFAULT 0, + `hours` float(3,1) unsigned NOT NULL DEFAULT 0.0, + `estimate` decimal(12,2) unsigned NOT NULL DEFAULT 0.00, + `consumed` decimal(12,2) unsigned NOT NULL DEFAULT 0.00, + `left` decimal(12,2) unsigned NOT NULL DEFAULT 0.00, + `order` tinyint(3) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `team` (`root`,`type`,`account`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testreport` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `tasks` varchar(255) NOT NULL DEFAULT '', + `builds` varchar(255) NOT NULL DEFAULT '', + `title` varchar(255) NOT NULL DEFAULT '', + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `owner` char(30) NOT NULL DEFAULT '', + `members` text DEFAULT NULL, + `stories` text DEFAULT NULL, + `bugs` text DEFAULT NULL, + `cases` text DEFAULT NULL, + `report` text DEFAULT NULL, + `objectType` varchar(20) NOT NULL DEFAULT '', + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testresult` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `run` mediumint(8) unsigned NOT NULL DEFAULT 0, + `case` mediumint(8) unsigned NOT NULL DEFAULT 0, + `version` smallint(5) unsigned NOT NULL DEFAULT 0, + `job` mediumint(8) unsigned NOT NULL DEFAULT 0, + `compile` mediumint(8) unsigned NOT NULL DEFAULT 0, + `caseResult` char(30) NOT NULL DEFAULT '', + `stepResults` text DEFAULT NULL, + `ZTFResult` text DEFAULT NULL, + `node` int(8) unsigned NOT NULL DEFAULT 0, + `lastRunner` varchar(30) NOT NULL DEFAULT '', + `date` datetime DEFAULT NULL, + `duration` float NOT NULL DEFAULT 0, + `xml` text DEFAULT NULL, + `deploy` mediumint(8) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `case` (`case`), + KEY `version` (`version`), + KEY `run` (`run`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testrun` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `task` mediumint(8) unsigned NOT NULL DEFAULT 0, + `case` mediumint(8) unsigned NOT NULL DEFAULT 0, + `version` tinyint(3) unsigned NOT NULL DEFAULT 0, + `assignedTo` char(30) NOT NULL DEFAULT '', + `lastRunner` varchar(30) NOT NULL DEFAULT '', + `lastRunDate` datetime DEFAULT NULL, + `lastRunResult` char(30) NOT NULL DEFAULT '', + `status` char(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `task` (`task`,`case`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testsuite` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `type` varchar(20) NOT NULL DEFAULT '', + `order` smallint(5) unsigned NOT NULL DEFAULT 0, + `addedBy` char(30) NOT NULL DEFAULT '', + `addedDate` datetime DEFAULT NULL, + `lastEditedBy` char(30) NOT NULL DEFAULT '', + `lastEditedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `product` (`product`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_testtask` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` char(90) NOT NULL DEFAULT '', + `execution` mediumint(8) unsigned NOT NULL DEFAULT 0, + `build` char(30) NOT NULL DEFAULT '', + `type` varchar(255) NOT NULL DEFAULT '', + `owner` varchar(30) NOT NULL DEFAULT '', + `pri` tinyint(3) unsigned NOT NULL DEFAULT 0, + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `realBegan` date DEFAULT NULL, + `realFinishedDate` datetime DEFAULT NULL, + `mailto` text DEFAULT NULL, + `desc` mediumtext DEFAULT NULL, + `report` text DEFAULT NULL, + `status` enum('blocked','doing','wait','done') NOT NULL DEFAULT 'wait', + `testreport` mediumint(8) unsigned NOT NULL DEFAULT 0, + `auto` varchar(10) NOT NULL DEFAULT 'no', + `subStatus` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `members` text DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `product` (`product`), + KEY `build` (`build`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ticket` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `product` mediumint(8) unsigned NOT NULL DEFAULT 0, + `module` mediumint(8) unsigned NOT NULL DEFAULT 0, + `title` varchar(255) NOT NULL DEFAULT '', + `type` varchar(30) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `openedBuild` varchar(255) NOT NULL DEFAULT '', + `feedback` mediumint(8) NOT NULL DEFAULT 0, + `assignedTo` varchar(255) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `realStarted` datetime DEFAULT NULL, + `startedBy` varchar(255) NOT NULL DEFAULT '', + `startedDate` datetime DEFAULT NULL, + `deadline` date DEFAULT NULL, + `pri` tinyint(3) unsigned NOT NULL DEFAULT 0, + `estimate` float unsigned NOT NULL DEFAULT 0, + `left` float unsigned NOT NULL DEFAULT 0, + `status` varchar(30) NOT NULL DEFAULT '', + `openedBy` varchar(30) NOT NULL DEFAULT '', + `openedDate` datetime DEFAULT NULL, + `activatedCount` int(11) NOT NULL DEFAULT 0, + `activatedBy` varchar(30) NOT NULL DEFAULT '', + `activatedDate` datetime DEFAULT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `closedReason` varchar(30) NOT NULL DEFAULT '', + `finishedBy` varchar(30) NOT NULL DEFAULT '', + `finishedDate` datetime DEFAULT NULL, + `resolvedBy` varchar(30) NOT NULL DEFAULT '', + `resolvedDate` datetime DEFAULT NULL, + `resolution` text DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `keywords` varchar(255) NOT NULL DEFAULT '', + `repeatTicket` mediumint(8) NOT NULL DEFAULT 0, + `mailto` varchar(255) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `subStatus` varchar(30) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + KEY `product` (`product`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ticketrelation` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `ticketId` mediumint(8) unsigned NOT NULL DEFAULT 0, + `objectId` mediumint(8) NOT NULL DEFAULT 0, + `objectType` varchar(100) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + KEY `ticketId` (`ticketId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_ticketsource` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `ticketId` mediumint(8) unsigned NOT NULL DEFAULT 0, + `customer` varchar(100) NOT NULL DEFAULT '', + `contact` varchar(100) NOT NULL DEFAULT '', + `notifyEmail` varchar(100) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `ticketId` (`ticketId`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_todo` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL DEFAULT '', + `date` date DEFAULT NULL, + `begin` smallint(4) unsigned zerofill NOT NULL DEFAULT 0000, + `end` smallint(4) unsigned zerofill NOT NULL DEFAULT 0000, + `feedback` mediumint(8) unsigned NOT NULL DEFAULT 0, + `type` char(15) NOT NULL DEFAULT '', + `cycle` tinyint(3) unsigned NOT NULL DEFAULT 0, + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `pri` tinyint(3) unsigned NOT NULL DEFAULT 0, + `name` char(150) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `status` enum('wait','doing','done','closed') NOT NULL DEFAULT 'wait', + `private` tinyint(1) NOT NULL DEFAULT 0, + `config` varchar(1000) NOT NULL DEFAULT '', + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedBy` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `finishedBy` varchar(30) NOT NULL DEFAULT '', + `finishedDate` datetime DEFAULT NULL, + `closedBy` varchar(30) NOT NULL DEFAULT '', + `closedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + PRIMARY KEY (`id`), + KEY `account` (`account`), + KEY `assignedTo` (`assignedTo`), + KEY `finishedBy` (`finishedBy`), + KEY `date` (`date`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_traincategory` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `name` char(30) NOT NULL DEFAULT '', + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `path` char(255) NOT NULL DEFAULT '', + `grade` tinyint(3) NOT NULL DEFAULT 0, + `order` mediumint(8) NOT NULL DEFAULT 0, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `parent` (`parent`), + KEY `path` (`path`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_traincontents` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `code` varchar(50) NOT NULL DEFAULT '', + `course` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `type` varchar(30) NOT NULL DEFAULT '', + `parent` mediumint(8) unsigned NOT NULL DEFAULT 0, + `path` char(255) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `order` mediumint(8) NOT NULL DEFAULT 0, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_traincourse` ( + `id` mediumint(8) NOT NULL AUTO_INCREMENT, + `code` varchar(255) NOT NULL DEFAULT '', + `category` mediumint(8) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `status` varchar(10) NOT NULL DEFAULT '', + `teacher` varchar(30) NOT NULL DEFAULT '', + `desc` mediumtext DEFAULT NULL, + `importedStatus` enum('','wait','doing','done') NOT NULL DEFAULT '', + `lastUpdatedTime` int(10) unsigned NOT NULL DEFAULT 0, + `createdBy` varchar(255) NOT NULL DEFAULT '', + `createdDate` date DEFAULT NULL, + `editedBy` varchar(255) NOT NULL DEFAULT '', + `editedDate` date DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_trainplan` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `place` varchar(255) NOT NULL DEFAULT '', + `trainee` text DEFAULT NULL, + `lecturer` varchar(20) NOT NULL DEFAULT '', + `type` enum('inside','outside') NOT NULL DEFAULT 'inside', + `status` varchar(20) NOT NULL DEFAULT '', + `summary` mediumtext DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_trainrecords` ( + `user` char(30) NOT NULL DEFAULT '', + `objectId` mediumint(8) unsigned NOT NULL DEFAULT 0, + `objectType` varchar(10) NOT NULL DEFAULT '', + `status` varchar(10) NOT NULL DEFAULT '', + PRIMARY KEY (`user`,`objectId`,`objectType`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_trip` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` enum('trip','egress') NOT NULL DEFAULT 'trip', + `customers` varchar(20) NOT NULL DEFAULT '', + `name` char(30) NOT NULL DEFAULT '', + `desc` text DEFAULT NULL, + `year` char(4) NOT NULL DEFAULT '', + `begin` date DEFAULT NULL, + `end` date DEFAULT NULL, + `start` time DEFAULT NULL, + `finish` time DEFAULT NULL, + `from` char(50) NOT NULL DEFAULT '', + `to` char(50) NOT NULL DEFAULT '', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `year` (`year`), + KEY `createdBy` (`createdBy`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_user` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `company` mediumint(8) unsigned NOT NULL DEFAULT 0, + `type` char(30) NOT NULL DEFAULT 'inside', + `dept` mediumint(8) unsigned NOT NULL DEFAULT 0, + `account` char(30) NOT NULL DEFAULT '', + `password` char(32) NOT NULL DEFAULT '', + `role` char(10) NOT NULL DEFAULT '', + `realname` varchar(100) NOT NULL DEFAULT '', + `superior` char(30) DEFAULT '', + `pinyin` varchar(255) NOT NULL DEFAULT '', + `nickname` char(60) NOT NULL DEFAULT '', + `commiter` varchar(100) NOT NULL DEFAULT '', + `avatar` text DEFAULT NULL, + `birthday` date DEFAULT NULL, + `gender` enum('f','m') NOT NULL DEFAULT 'f', + `email` char(90) NOT NULL DEFAULT '', + `skype` char(90) NOT NULL DEFAULT '', + `qq` char(20) NOT NULL DEFAULT '', + `mobile` char(11) NOT NULL DEFAULT '', + `phone` char(20) NOT NULL DEFAULT '', + `weixin` varchar(90) NOT NULL DEFAULT '', + `dingding` varchar(90) NOT NULL DEFAULT '', + `slack` varchar(90) NOT NULL DEFAULT '', + `whatsapp` varchar(90) NOT NULL DEFAULT '', + `address` char(120) NOT NULL DEFAULT '', + `zipcode` char(10) NOT NULL DEFAULT '', + `nature` text DEFAULT NULL, + `analysis` text DEFAULT NULL, + `strategy` text DEFAULT NULL, + `join` date DEFAULT NULL, + `visits` mediumint(8) unsigned NOT NULL DEFAULT 0, + `visions` varchar(20) NOT NULL DEFAULT 'rnd,lite', + `ip` varchar(255) NOT NULL DEFAULT '', + `last` int(11) unsigned NOT NULL DEFAULT 0, + `fails` tinyint(5) NOT NULL DEFAULT 0, + `locked` datetime DEFAULT NULL, + `feedback` enum('0','1') NOT NULL DEFAULT '0', + `ranzhi` char(30) NOT NULL DEFAULT '', + `ldap` char(30) NOT NULL DEFAULT '', + `score` int(11) NOT NULL DEFAULT 0, + `scoreLevel` int(11) NOT NULL DEFAULT 0, + `resetToken` varchar(50) NOT NULL DEFAULT '', + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `clientStatus` enum('online','away','busy','offline','meeting') NOT NULL DEFAULT 'offline', + `clientLang` varchar(10) NOT NULL DEFAULT 'zh-cn', + PRIMARY KEY (`id`), + UNIQUE KEY `account` (`account`), + KEY `dept` (`dept`), + KEY `email` (`email`), + KEY `commiter` (`commiter`), + KEY `deleted` (`deleted`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_usercontact` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL DEFAULT '', + `listName` varchar(60) NOT NULL DEFAULT '', + `userList` text DEFAULT NULL, + `public` tinyint(1) NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `account` (`account`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_usergroup` ( + `account` char(30) NOT NULL DEFAULT '', + `group` mediumint(8) unsigned NOT NULL DEFAULT 0, + `project` text DEFAULT NULL, + UNIQUE KEY `account` (`account`,`group`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_userquery` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL DEFAULT '', + `module` varchar(30) NOT NULL DEFAULT '', + `title` varchar(90) NOT NULL DEFAULT '', + `form` text DEFAULT NULL, + `sql` text DEFAULT NULL, + `shortcut` enum('0','1') NOT NULL DEFAULT '0', + `common` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `account` (`account`), + KEY `module` (`module`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_usertpl` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `account` char(30) NOT NULL DEFAULT '', + `type` char(30) NOT NULL DEFAULT '', + `title` varchar(150) NOT NULL DEFAULT '', + `content` text DEFAULT NULL, + `public` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `account` (`account`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_userview` ( + `account` char(30) NOT NULL DEFAULT '', + `programs` mediumtext DEFAULT NULL, + `products` mediumtext DEFAULT NULL, + `projects` mediumtext DEFAULT NULL, + `sprints` mediumtext DEFAULT NULL, + UNIQUE KEY `account` (`account`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_webhook` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` varchar(15) NOT NULL DEFAULT 'default', + `name` varchar(50) NOT NULL DEFAULT '', + `url` varchar(255) NOT NULL DEFAULT '', + `domain` varchar(255) NOT NULL DEFAULT '', + `secret` varchar(255) NOT NULL DEFAULT '', + `contentType` varchar(30) NOT NULL DEFAULT 'application/json', + `sendType` enum('sync','async') NOT NULL DEFAULT 'sync', + `products` text DEFAULT NULL, + `executions` text DEFAULT NULL, + `params` varchar(100) NOT NULL DEFAULT '', + `actions` text DEFAULT NULL, + `desc` text DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_weeklyreport` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `weekStart` date DEFAULT NULL, + `pv` float(9,2) NOT NULL DEFAULT 0.00, + `ev` float(9,2) NOT NULL DEFAULT 0.00, + `ac` float(9,2) NOT NULL DEFAULT 0.00, + `sv` float(9,2) NOT NULL DEFAULT 0.00, + `cv` float(9,2) NOT NULL DEFAULT 0.00, + `staff` smallint(5) unsigned NOT NULL DEFAULT 0, + `progress` varchar(255) NOT NULL DEFAULT '', + `workload` varchar(255) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `week` (`project`,`weekStart`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workestimation` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `project` mediumint(8) unsigned NOT NULL DEFAULT 0, + `scale` decimal(10,2) unsigned NOT NULL DEFAULT 0.00, + `productivity` decimal(10,2) unsigned NOT NULL DEFAULT 0.00, + `duration` decimal(10,2) unsigned NOT NULL DEFAULT 0.00, + `unitLaborCost` decimal(10,2) unsigned NOT NULL DEFAULT 0.00, + `totalLaborCost` decimal(10,2) unsigned NOT NULL DEFAULT 0.00, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `assignedTo` varchar(30) NOT NULL DEFAULT '', + `assignedDate` datetime DEFAULT NULL, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + `dayHour` decimal(10,2) NOT NULL DEFAULT 0.00, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflow` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `parent` varchar(30) NOT NULL DEFAULT '', + `child` varchar(30) NOT NULL DEFAULT '', + `type` varchar(10) NOT NULL DEFAULT 'flow', + `navigator` varchar(10) NOT NULL DEFAULT '', + `app` varchar(20) NOT NULL DEFAULT '', + `position` varchar(30) NOT NULL DEFAULT '', + `module` varchar(30) NOT NULL DEFAULT '', + `table` varchar(50) NOT NULL DEFAULT '', + `name` varchar(30) NOT NULL DEFAULT '', + `titleField` varchar(30) NOT NULL DEFAULT '', + `contentField` text DEFAULT NULL, + `flowchart` text DEFAULT NULL, + `js` text DEFAULT NULL, + `css` text DEFAULT NULL, + `order` smallint(5) unsigned NOT NULL DEFAULT 0, + `buildin` tinyint(1) unsigned NOT NULL DEFAULT 0, + `administrator` text DEFAULT NULL, + `desc` text DEFAULT NULL, + `version` varchar(10) NOT NULL DEFAULT '1.0', + `status` varchar(10) NOT NULL DEFAULT 'wait', + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `approval` enum('enabled','disabled') NOT NULL DEFAULT 'disabled', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unique` (`app`,`module`,`vision`), + KEY `type` (`type`), + KEY `app` (`app`), + KEY `module` (`module`), + KEY `order` (`order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowaction` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` varchar(30) NOT NULL DEFAULT '', + `action` varchar(50) NOT NULL DEFAULT '', + `method` varchar(50) NOT NULL DEFAULT '', + `name` varchar(50) NOT NULL DEFAULT '', + `type` enum('single','batch') NOT NULL DEFAULT 'single', + `batchMode` enum('same','different') NOT NULL DEFAULT 'different', + `extensionType` varchar(10) NOT NULL DEFAULT 'override', + `open` varchar(20) NOT NULL DEFAULT '', + `position` enum('menu','browseandview','browse','view') NOT NULL DEFAULT 'browseandview', + `layout` char(20) NOT NULL DEFAULT '', + `show` enum('dropdownlist','direct') NOT NULL DEFAULT 'dropdownlist', + `order` smallint(5) unsigned NOT NULL DEFAULT 0, + `buildin` tinyint(1) unsigned NOT NULL DEFAULT 0, + `role` varchar(10) NOT NULL DEFAULT 'custom', + `virtual` tinyint(1) unsigned NOT NULL DEFAULT 0, + `conditions` text DEFAULT NULL, + `verifications` text DEFAULT NULL, + `hooks` text DEFAULT NULL, + `linkages` text DEFAULT NULL, + `js` text DEFAULT NULL, + `css` text DEFAULT NULL, + `toList` char(255) NOT NULL DEFAULT '', + `blocks` text DEFAULT NULL, + `desc` text DEFAULT NULL, + `status` varchar(10) NOT NULL DEFAULT 'enable', + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unique` (`module`,`action`,`vision`), + KEY `module` (`module`), + KEY `action` (`action`), + KEY `order` (`order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowdatasource` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` enum('system','sql','func','option','lang','category') NOT NULL DEFAULT 'option', + `name` varchar(30) NOT NULL DEFAULT '', + `code` varchar(30) NOT NULL DEFAULT '', + `datasource` text DEFAULT NULL, + `view` varchar(20) NOT NULL DEFAULT '', + `keyField` varchar(50) NOT NULL DEFAULT '', + `valueField` varchar(50) NOT NULL DEFAULT '', + `buildin` tinyint(1) unsigned NOT NULL DEFAULT 0, + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` char(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowfield` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` varchar(30) NOT NULL DEFAULT '', + `field` varchar(50) NOT NULL DEFAULT '', + `type` varchar(20) NOT NULL DEFAULT 'varchar', + `length` varchar(10) NOT NULL DEFAULT '', + `name` varchar(50) NOT NULL DEFAULT '', + `control` varchar(20) NOT NULL DEFAULT '', + `expression` text DEFAULT NULL, + `options` text DEFAULT NULL, + `default` varchar(100) NOT NULL DEFAULT '', + `rules` varchar(255) NOT NULL DEFAULT '', + `placeholder` varchar(100) NOT NULL DEFAULT '', + `order` smallint(5) unsigned NOT NULL DEFAULT 0, + `searchOrder` smallint(5) unsigned NOT NULL DEFAULT 0, + `exportOrder` smallint(5) unsigned NOT NULL DEFAULT 0, + `canExport` enum('0','1') NOT NULL DEFAULT '0', + `canSearch` enum('0','1') NOT NULL DEFAULT '0', + `isValue` enum('0','1') NOT NULL DEFAULT '0', + `readonly` enum('0','1') NOT NULL DEFAULT '0', + `buildin` tinyint(1) unsigned NOT NULL DEFAULT 0, + `role` varchar(10) NOT NULL DEFAULT 'custom', + `desc` text DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unique` (`module`,`field`), + KEY `module` (`module`), + KEY `field` (`field`), + KEY `order` (`order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowlabel` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` varchar(30) NOT NULL DEFAULT '', + `action` varchar(30) NOT NULL DEFAULT 'browse', + `code` varchar(30) NOT NULL DEFAULT '', + `label` varchar(255) NOT NULL DEFAULT '', + `params` text DEFAULT NULL, + `orderBy` text DEFAULT NULL, + `order` tinyint(3) NOT NULL DEFAULT 0, + `buildin` tinyint(1) unsigned NOT NULL DEFAULT 0, + `role` varchar(10) NOT NULL DEFAULT 'custom', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` char(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `module` (`module`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowlayout` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` varchar(30) NOT NULL DEFAULT '', + `action` varchar(50) NOT NULL DEFAULT '', + `field` varchar(50) NOT NULL DEFAULT '', + `order` smallint(5) unsigned NOT NULL DEFAULT 0, + `width` varchar(50) NOT NULL DEFAULT '0', + `position` text DEFAULT NULL, + `readonly` enum('0','1') NOT NULL DEFAULT '0', + `mobileShow` enum('0','1') NOT NULL DEFAULT '1', + `summary` varchar(20) NOT NULL DEFAULT '', + `defaultValue` text DEFAULT NULL, + `layoutRules` varchar(255) NOT NULL DEFAULT '', + `vision` varchar(10) NOT NULL DEFAULT 'rnd', + PRIMARY KEY (`id`), + UNIQUE KEY `unique` (`module`,`action`,`field`,`vision`), + KEY `module` (`module`), + KEY `action` (`action`), + KEY `order` (`order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowlinkdata` ( + `objectType` varchar(30) NOT NULL DEFAULT '', + `objectID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `linkedType` varchar(30) NOT NULL DEFAULT '', + `linkedID` mediumint(8) unsigned NOT NULL DEFAULT 0, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + UNIQUE KEY `unique` (`objectType`,`objectID`,`linkedType`,`linkedID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowrelation` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `prev` varchar(30) NOT NULL DEFAULT '', + `next` varchar(30) NOT NULL DEFAULT '', + `field` varchar(50) NOT NULL DEFAULT '', + `actions` varchar(20) NOT NULL DEFAULT '', + `actionCodes` text DEFAULT NULL, + `buildin` enum('0','1') NOT NULL DEFAULT '0', + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowrelationlayout` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `prev` varchar(30) NOT NULL DEFAULT '', + `next` varchar(30) NOT NULL DEFAULT '', + `action` varchar(50) NOT NULL DEFAULT '', + `field` varchar(50) NOT NULL DEFAULT '', + `order` smallint(5) unsigned NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `unique` (`prev`,`next`,`action`,`field`), + KEY `prev` (`prev`), + KEY `next` (`next`), + KEY `action` (`action`), + KEY `order` (`order`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowreport` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` varchar(30) NOT NULL, + `name` varchar(100) NOT NULL, + `type` enum('pie','line','bar') NOT NULL DEFAULT 'pie', + `countType` enum('sum','count') NOT NULL DEFAULT 'sum', + `displayType` enum('value','percent') NOT NULL DEFAULT 'value', + `dimension` varchar(130) NOT NULL, + `fields` text NOT NULL, + `order` smallint(5) unsigned NOT NULL DEFAULT 0, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowrule` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `type` enum('system','regex','func') NOT NULL DEFAULT 'regex', + `name` varchar(30) NOT NULL DEFAULT '', + `rule` text DEFAULT NULL, + `createdBy` char(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` char(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `type` (`type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowsql` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` varchar(30) NOT NULL DEFAULT '', + `field` varchar(50) NOT NULL DEFAULT '', + `action` varchar(50) NOT NULL DEFAULT '', + `sql` text DEFAULT NULL, + `vars` text DEFAULT NULL, + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `module` (`module`), + KEY `field` (`field`), + KEY `action` (`action`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_workflowversion` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` varchar(30) NOT NULL DEFAULT '', + `version` varchar(10) NOT NULL DEFAULT '', + `fields` text DEFAULT NULL, + `actions` text DEFAULT NULL, + `layouts` text DEFAULT NULL, + `sqls` text DEFAULT NULL, + `labels` text DEFAULT NULL, + `table` text DEFAULT NULL, + `datas` text DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `moduleversion` (`module`,`version`), + KEY `module` (`module`), + KEY `version` (`version`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE TABLE `zt_zoutput` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `activity` mediumint(8) NOT NULL DEFAULT 0, + `name` varchar(255) NOT NULL DEFAULT '', + `content` mediumtext DEFAULT NULL, + `optional` char(20) NOT NULL DEFAULT '', + `tailorNorm` varchar(255) NOT NULL DEFAULT '', + `status` varchar(30) NOT NULL DEFAULT '', + `createdBy` varchar(30) NOT NULL DEFAULT '', + `createdDate` datetime DEFAULT NULL, + `editedBy` varchar(30) NOT NULL DEFAULT '', + `editedDate` datetime DEFAULT NULL, + `order` mediumint(8) DEFAULT 0, + `deleted` enum('0','1') NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/db/update20.0.sql b/db/update20.0.sql index 028439a95e..33a599c697 100644 --- a/db/update20.0.sql +++ b/db/update20.0.sql @@ -41,12 +41,3 @@ UPDATE `zt_kanbancard` SET `color` = '#ff9f46' WHERE `color` = '#2a5f29'; INSERT INTO `zt_cron`(`m`, `h`, `dom`, `mon`, `dow`, `command`, `remark`, `type`, `buildin`, `status`, `lastTime`) VALUES ('0', '*/1', '*', '*', '*', 'moduleName=metric&methodName=updateDashboardMetricLib', '计算仪表盘数据', 'zentao', 1, 'normal', NUll); UPDATE `zt_todo` SET `type` = 'custom' WHERE `type` = 'cycle' AND `cycle` = 0; - -INSERT INTO `zt_metric` (`purpose`, `scope`, `object`, `stage`, `type`, `name`, `code`, `alias`, `unit`, `desc`, `definition`, `when`, `createdBy`, `createdDate`, `builtin`, `deleted`, `dateType`) VALUES -('scale', 'product', 'feedback', 'released', 'php', '按产品统计的每周新增反馈数', 'count_of_weekly_created_feedback_in_product', '新增反馈数', 'count', '按产品统计的每周新增反馈数是指在一个周内收集到的用户反馈的数量。这个度量项可以帮助团队了解用户对产品的发展趋势和需求变化,并进行产品策略的调整和优化', '产品中创建时间为某个周的反馈的个数求和\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'week'), -('scale', 'product', 'feedback', 'released', 'php', '按产品统计的处理中的反馈数', 'count_of_doing_feedback_in_product', '处理中反馈数', 'count', '按产品统计的处理中的反馈数表示产品中状态为处理中的反馈数量之和。该数值越大,说明团队并行处理的反馈越多,可以帮助团队了解当前的工作负载情况', '产品中所有反馈个数求和\r\n状态为处理中\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'), -('scale', 'product', 'feedback', 'released', 'php', '按产品统计的已处理的反馈数', 'count_of_done_feedback_in_product', '已处理反馈数', 'count', '按产品统计的已处理的反馈数表示产品中状态为已处理的反馈数量之和。该数值越大,说明团队成员处理的反馈越多,有利于提高用户满意度', '产品中所有反馈个数求和\r\n状态为已处理\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'), -('scale', 'product', 'feedback', 'released', 'php', '按产品统计的待完善的反馈数', 'count_of_clarify_feedback_in_product', '待完善反馈数', 'count', '按产品统计的待完善的反馈数表示产品中状态为待完善的反馈数量之和。该数值越大,说明有较多的反馈信息不清晰或比较复杂。需要反馈者更多的澄清和解释', '产品中所有反馈个数求和\r\n状态为待完善\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'), -('scale', 'product', 'feedback', 'released', 'php', '按产品统计的待处理的反馈数', 'count_of_wait_feedback_in_product', '待处理反馈数', 'count', '按产品统计的待处理的反馈数表示产品中状态为待处理的反馈数量之和。该度量项可能暗示产品团队的反馈处理效率,待处理反馈数越多,可能会导致客户满意度降低', '产品中所有反馈个数求和\r\n状态为待处理\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'), -('scale', 'product', 'feedback', 'released', 'php', '按产品统计的追问中的反馈数', 'count_of_asked_feedback_in_product', '追问中反馈数', 'count', '按产品统计的追问中的反馈数表示产品中状态为追问中的反馈数量之和。该度量项可能暗示着反馈的复杂性或对处理方案的疑惑,追问中的反馈数量越多,可能意味着团队需要更多时间和资源来回复并解决这些问题', '产品中所有反馈个数求和\r\n状态为追问中\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'), -('scale', 'product', 'feedback', 'released', 'php', '按产品统计的未关闭的反馈数', 'count_of_unclosed_feedback_in_product', '未关闭反馈数', 'count', '按产品统计的未关闭的反馈数表示产品中状态为未关闭的反馈数量之和。这个度量项可以一定程度反映产品团队响应用户反馈的效率和及时处理用户问题的能力', '产品中所有反馈个数求和\r\n过滤状态为已关闭的反馈\r\n过滤已删除的反馈\r\n过滤已删除的产品', 'realtime', 'system', '2024-05-07 08:00:00', '1', '0', 'nodate'); diff --git a/db/update20.5.sql b/db/update20.5.sql new file mode 100644 index 0000000000..62dc115b7a --- /dev/null +++ b/db/update20.5.sql @@ -0,0 +1,37 @@ +CREATE TABLE IF NOT EXISTS `zt_workflowui` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` varchar(30) NOT NULL, + `action` varchar(50) NOT NULL, + `name` varchar(30) NOT NULL, + `conditions` text NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE INDEX `module` ON `zt_workflowui` (`module`); +CREATE INDEX `action` ON `zt_workflowui` (`action`); + +ALTER TABLE `zt_workflowlayout` ADD `ui` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `action`; +ALTER TABLE `zt_workflowrelationlayout` ADD `ui` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `action`; + +ALTER TABLE `zt_approvalflow` CHANGE `type` `workflow` char(30) NOT NULL DEFAULT ''; +UPDATE `zt_approvalflow` SET `workflow` = ''; + +DROP INDEX `unique` ON `zt_workflowlayout`; +CREATE UNIQUE INDEX `unique` ON `zt_workflowlayout`(`module`,`action`,`ui`,`field`,`vision`); + +DROP INDEX `unique` ON `zt_workflowrelationlayout`; +CREATE UNIQUE INDEX `unique` ON `zt_workflowrelationlayout`(`prev`, `next`, `action`,`ui`,`field`); + +UPDATE `zt_workflowaction` SET `method`='create', `type`='single' WHERE `module`='epic' AND `action`='create'; +UPDATE `zt_workflowaction` SET `method`='batchcreate', `type`='batch' WHERE `module`='epic' AND `action`='batchcreate'; +UPDATE `zt_workflowaction` SET `method`='batchoperate', `type`='batch' WHERE `module`='epic' AND `action`='batchedit'; +UPDATE `zt_workflowaction` SET `method`='exporttemplate', `type`='single' WHERE `module`='epic' AND `action`='exporttemplate'; +UPDATE `zt_workflowaction` SET `method`='import', `type`='single' WHERE `module`='epic' AND `action`='import'; +UPDATE `zt_workflowaction` SET `method`='showimport', `type`='single' WHERE `module`='epic' AND `action`='showimport'; +UPDATE `zt_workflowaction` SET `method`='edit', `type`='single' WHERE `module`='epic' AND `action`='edit'; +UPDATE `zt_workflowaction` SET `method`='view', `type`='single' WHERE `module`='epic' AND `action`='view'; +UPDATE `zt_workflowaction` SET `method`='delete', `type`='single' WHERE `module`='epic' AND `action`='delete'; +UPDATE `zt_workflowaction` SET `method`='operate', `type`='single' WHERE `module`='epic' AND `action`='close'; +UPDATE `zt_workflowaction` SET `method`='operate', `type`='single' WHERE `module`='epic' AND `action`='activate'; +UPDATE `zt_workflowaction` SET `method`='operate', `type`='single' WHERE `module`='epic' AND `action`='assignTo'; +UPDATE `zt_workflowaction` SET `method`='operate', `type`='single' WHERE `module`='epic' AND `action`='review'; +UPDATE `zt_workflowaction` SET `method`='operate', `type`='single' WHERE `module`='epic' AND `action`='change'; diff --git a/db/zentao.sql b/db/zentao.sql index ac850cfad2..3195d4f56b 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -179,7 +179,7 @@ CREATE TABLE IF NOT EXISTS `zt_approvalflow` ( `version` mediumint(8) NOT NULL DEFAULT '1', `createdBy` varchar(30) NOT NULL DEFAULT '', `createdDate` datetime NULL, - `type` varchar(30) NOT NULL DEFAULT '', + `workflow` varchar(30) NOT NULL DEFAULT '', `deleted` tinyint(4) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -2440,256 +2440,6 @@ INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES (' INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'story', '', 'gradeRule', 'stepwise'); INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'requirement', '', 'gradeRule', 'stepwise'); INSERT INTO `zt_config` (`owner`, `module`, `section`, `key`, `value`) VALUES ('system', 'epic', '', 'gradeRule', 'stepwise'); --- DROP TABLE IF EXISTS `zt_im_chat`; -CREATE TABLE IF NOT EXISTS `zt_im_chat` ( - `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, - `gid` char(40) NOT NULL DEFAULT '', - `name` varchar(60) NOT NULL DEFAULT '', - `type` varchar(20) NOT NULL DEFAULT 'group', - `admins` varchar(255) NOT NULL DEFAULT '', - `committers` varchar(255) NOT NULL DEFAULT '', - `subject` mediumint(8) unsigned NOT NULL DEFAULT 0, - `public` enum('0', '1') NOT NULL DEFAULT '0', - `createdBy` varchar(30) NOT NULL DEFAULT '', - `createdDate` datetime NULL, - `ownedBy` varchar(30) NOT NULL DEFAULT '', - `editedBy` varchar(30) NOT NULL DEFAULT '', - `editedDate` datetime NULL, - `mergedDate` datetime NULL, - `lastActiveTime` datetime NULL, - `lastMessage` int(11) unsigned NOT NULL DEFAULT 0, - `lastMessageIndex` int(11) unsigned NOT NULL DEFAULT 0, - `dismissDate` datetime NULL, - `pinnedMessages` text NULL, - `mergedChats` text NULL, - `adminInvite` enum('0','1') NOT NULL DEFAULT '0', - `avatar` text NULL, - `archiveDate` datetime NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE INDEX `gid` ON `zt_im_chat` (`gid`); -CREATE INDEX `name` ON `zt_im_chat` (`name`); -CREATE INDEX `type` ON `zt_im_chat` (`type`); -CREATE INDEX `public` ON `zt_im_chat` (`public`); -CREATE INDEX `createdBy` ON `zt_im_chat` (`createdBy`); -CREATE INDEX `editedBy` ON `zt_im_chat` (`editedBy`); - --- DROP TABLE IF EXISTS `zt_im_chatuser`; -CREATE TABLE IF NOT EXISTS `zt_im_chatuser` ( - `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, - `cgid` char(40) NOT NULL DEFAULT '', - `user` mediumint(8) NOT NULL DEFAULT 0, - `order` smallint(5) NOT NULL DEFAULT 0, - `star` enum('0', '1') NOT NULL DEFAULT '0', - `hide` enum('0', '1') NOT NULL DEFAULT '0', - `mute` enum('0', '1') NOT NULL DEFAULT '0', - `freeze` enum('0', '1') NOT NULL DEFAULT '0', - `join` datetime NULL, - `quit` datetime NULL, - `category` varchar(40) NOT NULL DEFAULT '', - `lastReadMessage` int(11) unsigned NOT NULL DEFAULT 0, - `lastReadMessageIndex` int(11) unsigned NOT NULL DEFAULT 0, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE UNIQUE INDEX `chatuser` ON `zt_im_chatuser`(`cgid`, `user`); -CREATE INDEX `cgid` ON `zt_im_chatuser` (`cgid`); -CREATE INDEX `user` ON `zt_im_chatuser` (`user`); -CREATE INDEX `order` ON `zt_im_chatuser` (`order`); -CREATE INDEX `star` ON `zt_im_chatuser` (`star`); -CREATE INDEX `hide` ON `zt_im_chatuser` (`hide`); - --- DROP TABLE IF EXISTS `zt_im_client`; -CREATE TABLE IF NOT EXISTS `zt_im_client` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `version` char(30) NOT NULL DEFAULT '', - `desc` varchar(100) NOT NULL DEFAULT '', - `changeLog` text NULL, - `strategy` varchar(10) NOT NULL DEFAULT '', - `downloads` text NULL, - `createdDate` datetime NULL, - `createdBy` varchar(30) NOT NULL DEFAULT '', - `editedDate` datetime NULL, - `editedBy` varchar(30) NOT NULL DEFAULT '', - `status` enum('released','wait') NOT NULL DEFAULT 'wait', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- DROP TABLE IF EXISTS `zt_im_message`; -CREATE TABLE IF NOT EXISTS `zt_im_message` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `gid` char(40) NOT NULL DEFAULT '', - `cgid` char(40) NOT NULL DEFAULT '', - `user` varchar(30) NOT NULL DEFAULT '', - `date` datetime NULL, - `index` int(11) unsigned NOT NULL DEFAULT 0, - `type` enum('normal','broadcast','notify','bulletin','botcommand') NOT NULL DEFAULT 'normal', - `content` text NULL, - `contentType` enum('text', 'plain', 'emotion', 'image', 'file', 'object', 'code') NOT NULL DEFAULT 'text', - `data` text NULL, - `deleted` enum('0','1') NOT NULL DEFAULT '0', - `legacy` tinyint(1) NOT NULL DEFAULT 0, - `uniqueIndex` int(11) GENERATED ALWAYS AS (CASE WHEN `legacy` = 1 THEN NULL WHEN `cgid` = 'notification' THEN NULL ELSE `index` END) STORED, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE INDEX `mgid` ON `zt_im_message` (`gid`); -CREATE INDEX `mcgid` ON `zt_im_message` (`cgid`); -CREATE INDEX `muser` ON `zt_im_message` (`user`); -CREATE INDEX `mtype` ON `zt_im_message` (`type`); -CREATE UNIQUE INDEX `uniqueIndexInChat` ON `zt_im_message`(`cgid`, `uniqueIndex`); - --- DROP TABLE IF EXISTS `zt_im_message_backup`; -CREATE TABLE IF NOT EXISTS `zt_im_message_backup` ( - `id` int(11) unsigned NOT NULL DEFAULT '0', - `gid` char(40) NOT NULL DEFAULT '', - `cgid` char(40) NOT NULL DEFAULT '', - `user` varchar(30) NOT NULL DEFAULT '', - `date` datetime NULL, - `index` int(11) unsigned NOT NULL DEFAULT 0, - `type` enum('normal', 'broadcast', 'notify') NOT NULL DEFAULT 'normal', - `content` text NULL, - `contentType` enum('text', 'plain', 'emotion', 'image', 'file', 'object', 'code') NOT NULL DEFAULT 'text', - `data` text NULL, - `deleted` enum('0','1') NOT NULL DEFAULT '0' -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- DROP TABLE IF EXISTS `zt_im_message_index`; -CREATE TABLE IF NOT EXISTS `zt_im_message_index` ( - `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, - `tableName` char(64) NOT NULL DEFAULT '', - `start` int(11) unsigned NOT NULL DEFAULT '0', - `end` int(11) unsigned NOT NULL DEFAULT '0', - `startDate` datetime NULL, - `endDate` datetime NULL, - `chats` text NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE INDEX `tableName` ON `zt_im_message_index` (`tableName`); -CREATE INDEX `start` ON `zt_im_message_index` (`start`); -CREATE INDEX `end` ON `zt_im_message_index` (`end`); -CREATE INDEX `startDate` ON `zt_im_message_index` (`startDate`); -CREATE INDEX `endDate` ON `zt_im_message_index` (`endDate`); - --- DROP TABLE IF EXISTS `zt_im_chat_message_index`; -CREATE TABLE IF NOT EXISTS `zt_im_chat_message_index` ( - `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, - `gid` char(40) NOT NULL DEFAULT '', - `tableName` char(64) NOT NULL DEFAULT '', - `start` int(11) unsigned NOT NULL DEFAULT '0', - `end` int(11) unsigned NOT NULL DEFAULT '0', - `startIndex` int(11) unsigned NOT NULL DEFAULT '0', - `endIndex` int(11) unsigned NOT NULL DEFAULT '0', - `startDate` datetime NULL, - `endDate` datetime NULL, - `count` mediumint(8) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE UNIQUE INDEX `chattable` ON `zt_im_chat_message_index`(`gid`,`tableName`); -CREATE INDEX `start` ON `zt_im_chat_message_index` (`start`); -CREATE INDEX `end` ON `zt_im_chat_message_index` (`end`); -CREATE INDEX `startDate` ON `zt_im_chat_message_index` (`startDate`); -CREATE INDEX `endDate` ON `zt_im_chat_message_index` (`endDate`); -CREATE INDEX `chatstartindex` ON `zt_im_chat_message_index` (`gid`, `startIndex`); -CREATE INDEX `chatendindex` ON `zt_im_chat_message_index` (`gid`, `endIndex`); - --- DROP TABLE IF EXISTS `zt_im_messagestatus`; -CREATE TABLE IF NOT EXISTS `zt_im_messagestatus` ( - `user` mediumint(8) NOT NULL DEFAULT 0, - `message` int(11) unsigned NOT NULL DEFAULT '0', - `status` enum('waiting','sent','readed','deleted') NOT NULL DEFAULT 'waiting' -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE UNIQUE INDEX `user` ON `zt_im_messagestatus`(`user`,`message`); - --- DROP TABLE IF EXISTS `zt_im_queue`; -CREATE TABLE IF NOT EXISTS `zt_im_queue` ( - `id` mediumint(8) unsigned NOT NULL auto_increment, - `type` char(30) NOT NULL DEFAULT '', - `content` text NULL, - `addDate` datetime NULL, - `processDate` datetime NULL, - `result` text NULL, - `status` char(30) NOT NULL DEFAULT '', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- DROP TABLE IF EXISTS `zt_im_conference`; -CREATE TABLE IF NOT EXISTS `zt_im_conference` ( - `id` mediumint(8) unsigned NOT NULL auto_increment, - `rid` char(40) NOT NULL DEFAULT '', - `cgid` char(40) NOT NULL DEFAULT '', - `status` enum ('closed', 'open', 'notStarted', 'canceled') NOT NULL DEFAULT 'closed', - `participants` text NULL, - `subscribers` text NULL, - `invitee` text NULL, - `openedBy` mediumint(8) NOT NULL DEFAULT 0, - `openedDate` datetime NULL, - `topic` text NULL, - `startTime` datetime NULL, - `endTime` datetime NULL, - `password` char(20) NOT NULL DEFAULT '', - `type` enum('default', 'periodic', 'scheduled') NOT NULL DEFAULT 'default', - `number` char(20) NOT NULL DEFAULT '', - `note` text NULL, - `sentNotify` tinyint(1) NOT NULL DEFAULT 0, - `reminderTime` int NOT NULL DEFAULT 0, - `moderators` text NOT NULL, - `isPrivate` enum ('0', '1') NOT NULL DEFAULT '0', - `isInner` enum('0', '1') NOT NULL DEFAULT '1', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE INDEX `status` ON `zt_im_conference` (`status`); - --- DROP TABLE IF EXISTS `zt_im_conferenceaction`; -CREATE TABLE IF NOT EXISTS `zt_im_conferenceaction` ( - `id` mediumint(8) unsigned NOT NULL auto_increment, - `rid` char(40) NOT NULL DEFAULT '', - `type` enum('create','invite','join','leave','close','publish') NOT NULL DEFAULT 'create', - `data` text NULL, - `user` mediumint(8) NOT NULL DEFAULT 0, - `date` datetime NULL, - `device` char(40) NOT NULL DEFAULT 'default', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - --- DROP TABLE IF EXISTS `zt_im_conferenceuser`; -CREATE TABLE IF NOT EXISTS `zt_im_conferenceuser` ( - `id` mediumint(8) unsigned NOT NULL auto_increment, - `conference` mediumint(8) NOT NULL DEFAULT 0, - `user` mediumint(8) NOT NULL DEFAULT 0, - `hide` enum('0', '1') NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE UNIQUE INDEX `conferenceuser` ON `zt_im_conferenceuser`(`conference`, `user`); - --- DROP TABLE IF EXISTS `zt_im_conferenceinvite`; -CREATE TABLE IF NOT EXISTS `zt_im_conferenceinvite` ( - `id` mediumint(8) unsigned NOT NULL auto_increment, - `conferenceID` mediumint(8) unsigned NOT NULL, - `inviteeID` mediumint(8) unsigned NOT NULL, - `status` enum('pending', 'accepted', 'rejected') NOT NULL DEFAULT 'pending', - `createdDate` datetime NULL, - `updatedDate` datetime NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE UNIQUE INDEX `conference_user` ON `zt_im_conferenceinvite` (`conferenceID`, `inviteeID`); - --- DROP TABLE IF EXISTS `zt_im_userdevice`; -CREATE TABLE IF NOT EXISTS `zt_im_userdevice` ( - `id` mediumint(8) unsigned NOT NULL auto_increment, - `user` mediumint(8) NOT NULL DEFAULT 0, - `device` char(40) NOT NULL DEFAULT 'default', - `deviceID` char(40) NOT NULL DEFAULT '', - `token` char(64) NOT NULL DEFAULT '', - `validUntil` datetime NULL, - `lastLogin` datetime NULL, - `lastLogout` datetime NULL, - `online` tinyint(1) DEFAULT 0 NOT NULL, - `version` char(10) DEFAULT '' NOT NULL, - PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE UNIQUE INDEX `userdevice` ON `zt_im_userdevice`(`user`, `device`); -CREATE INDEX `user` ON `zt_im_userdevice` (`user`); -CREATE INDEX `lastLogin` ON `zt_im_userdevice` (`lastLogin`); -CREATE INDEX `lastLogout` ON `zt_im_userdevice` (`lastLogout`); -- DROP TABLE IF EXISTS `zt_relationoftasks`; CREATE TABLE IF NOT EXISTS `zt_relationoftasks` ( @@ -13572,6 +13322,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflowlayout` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `module` varchar(30) NOT NULL DEFAULT '', `action` varchar(50) NOT NULL DEFAULT '', + `ui` mediumint(8) NOT NULL DEFAULT 0, `field` varchar(50) NOT NULL DEFAULT '', `order` smallint(5) unsigned NOT NULL DEFAULT '0', `width` varchar(50) NOT NULL DEFAULT '0', @@ -13584,7 +13335,7 @@ CREATE TABLE IF NOT EXISTS `zt_workflowlayout` ( `vision` varchar(10) NOT NULL DEFAULT 'rnd', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE UNIQUE INDEX `unique` ON `zt_workflowlayout`(`module`,`action`,`field`,`vision`); +CREATE UNIQUE INDEX `unique` ON `zt_workflowlayout`(`module`,`action`,`ui`,`field`,`vision`); CREATE INDEX `module` ON `zt_workflowlayout` (`module`); CREATE INDEX `action` ON `zt_workflowlayout` (`action`); CREATE INDEX `order` ON `zt_workflowlayout` (`order`); @@ -13640,11 +13391,12 @@ CREATE TABLE IF NOT EXISTS `zt_workflowrelationlayout` ( `prev` varchar(30) NOT NULL DEFAULT '', `next` varchar(30) NOT NULL DEFAULT '', `action` varchar(50) NOT NULL DEFAULT '', + `ui` mediumint(8) NOT NULL DEFAULT 0, `field` varchar(50) NOT NULL DEFAULT '', `order` smallint(5) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -CREATE UNIQUE INDEX `unique` ON `zt_workflowrelationlayout`(`prev`, `next`, `action`, `field`); +CREATE UNIQUE INDEX `unique` ON `zt_workflowrelationlayout`(`prev`, `next`, `action`,`ui`,`field`); CREATE INDEX `prev` ON `zt_workflowrelationlayout` (`prev`); CREATE INDEX `next` ON `zt_workflowrelationlayout` (`next`); CREATE INDEX `action` ON `zt_workflowrelationlayout` (`action`); @@ -13700,6 +13452,18 @@ CREATE UNIQUE INDEX `moduleversion` ON `zt_workflowversion`(`module`, `version`) CREATE INDEX `module` ON `zt_workflowversion` (`module`); CREATE INDEX `version` ON `zt_workflowversion` (`version`); +-- DROP TABLE IF EXISTS `zt_workflowui`; +CREATE TABLE IF NOT EXISTS `zt_workflowui` ( + `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, + `module` varchar(30) NOT NULL, + `action` varchar(50) NOT NULL, + `name` varchar(30) NOT NULL, + `conditions` text NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +CREATE INDEX `module` ON `zt_workflowui` (`module`); +CREATE INDEX `action` ON `zt_workflowui` (`action`); + -- DROP TABLE IF EXISTS `zt_workflowreport`; CREATE TABLE IF NOT EXISTS `zt_workflowreport` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, @@ -14689,8 +14453,8 @@ CREATE TABLE `zt_scene` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -REPLACE INTO `zt_approvalflow` (`id`, `name`, `code`, `desc`, `version`, `createdBy`, `createdDate`, `type`, `deleted`) VALUES -(1, '最简审批', 'simple', '', 1, 'admin', '2022-04-29 08:46:40', 'project', 0); +REPLACE INTO `zt_approvalflow` (`id`, `name`, `code`, `desc`, `version`, `createdBy`, `createdDate`, `workflow`, `deleted`) VALUES +(1, '最简审批', 'simple', '', 1, 'admin', '2022-04-29 08:46:40', '', 0); REPLACE INTO `zt_approvalflowspec` (`id`, `flow`, `version`, `nodes`, `createdBy`, `createdDate`) VALUES (1, 1, 1, '[{\"type\":\"start\",\"ccs\":[]},{\"id\":\"3ewcj92p55e\",\"type\":\"approval\",\"title\":\"审批\",\"reviewType\":\"manual\",\"multiple\":\"and\",\"agentType\":\"pass\",\"reviewers\":[{\"type\":\"select\"}],\"ccs\":[]},{\"type\":\"end\",\"ccs\":[]}]', 'admin', '2022-04-29 08:46:40'); diff --git a/doc/CHANGELOG b/doc/CHANGELOG index 1ad9679a93..5eb54bdf07 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -1,3 +1,7 @@ +2024-08-30 20.6 +完成的需求 +修复的Bug + 2024-08-16 20.5 完成的需求: 开源版: @@ -155,6 +159,43 @@ IPD版: 53821 任务列表不显示导出模版、导入excel按钮 +2024-08-09 18.13 +修复的Bug: +开源版: +52925 测试单无法导出用例信息 +53054 [达梦]18版本燃尽图修改首日工时不生效 +53410 任务标题输入空格能直接保存成功 +53580 已解决的Bug,解决版本调整后没有将Bug自动关联至调整后版本的解决的Bug中 +53594 下载聊天后端error +53623 运营界面没有导出用户的相关权限 +企业版: +53217 用户需求通过工作流对评审页面增加字段不显示 +53572 8.13升级报错 +53585 测试单的日志剩余工时不显示 +旗舰版: +52928 BI中报表拖动分组顺序不生效 +52972 达梦数据库兼容问题导致工作流有些模块无法应用 +53023 [达梦] 项目仪表盘项目周报区块报错 +53024 [达梦] 项目仪表盘估算区块报错 +53025 [达梦] 产品—发布详情页面报错 +53082 [达梦] BI图表查询数据失败 +53104 [达梦]BI数据表保存报错502 +53105 【达梦】数据表,点击中间表报错502 +53106 【达梦】重算全部度量项页面报错 +53232 【达梦】第一次设置审批流点击保存报错 +53233 【达梦】关闭审批流报错 +53240 【达梦】看板切换泳道方式报错 +53312 工作流审批选不到审批人 +53447 需求导出页面报错 +53582 开源版1253升级到旗舰版4.13报错 +IPD版: +53612 开始市场阶段出现报错 +53613 编辑调研阶段保存时提示“里程碑不符合格式” +53614 设置了阶段可以并行时,执行下添加的IPD项目子阶段无法开始 +53620 OR界面导出用户内容为空 +53622 轻量模式-无执行项目需求批量转任务保存有报错 +53644 ipd1.5升级到ipd2.1出现了报错 + 2024-08-02 20.4 完成的需求: 开源版: diff --git a/extension/lite/group/ext/lang/resource.php b/extension/lite/group/ext/lang/resource.php index a33b39d561..918d48302d 100644 --- a/extension/lite/group/ext/lang/resource.php +++ b/extension/lite/group/ext/lang/resource.php @@ -1038,13 +1038,21 @@ if($config->edition != 'open') /* Feedback. */ $lang->resource->feedback = new stdclass(); $lang->resource->feedback->create = 'create'; + $lang->resource->feedback->batchCreate = 'batchCreate'; $lang->resource->feedback->edit = 'edit'; $lang->resource->feedback->browse = 'browse'; $lang->resource->feedback->view = 'view'; $lang->resource->feedback->comment = 'comment'; + $lang->resource->feedback->ask = 'ask'; $lang->resource->feedback->delete = 'delete'; $lang->resource->feedback->close = 'closeAction'; + $lang->resource->feedback->activate = 'activate'; + $lang->resource->feedback->batchEdit = 'batchEdit'; + $lang->resource->feedback->batchClose = 'batchClose'; + $lang->resource->feedback->batchAssignTo = 'batchAssignTo'; $lang->resource->feedback->export = 'exportAction'; + $lang->resource->feedback->import = 'import'; + $lang->resource->feedback->exportTemplate = 'exportTemplate'; $lang->resource->feedback->assignTo = 'assignAction'; $lang->resource->feedback->products = 'products'; $lang->resource->feedback->manageProduct = 'manageProduct'; @@ -1069,6 +1077,11 @@ if($config->edition != 'open') $lang->resource->ticket->exportTemplate = 'exportTemplate'; $lang->resource->ticket->import = 'import'; $lang->resource->ticket->export = 'exportAction'; + $lang->resource->ticket->batchCreate = 'batchCreate'; + $lang->resource->ticket->batchEdit = 'batchEdit'; + $lang->resource->ticket->batchActivate = 'batchActivate'; + $lang->resource->ticket->batchFinish = 'batchFinish'; + $lang->resource->ticket->batchAssignTo = 'batchAssignTo'; /* Attend */ $lang->resource->attend = new stdclass(); @@ -1349,11 +1362,18 @@ if($config->edition != 'open') /* workflowlayout */ $lang->resource->workflowlayout = new stdclass(); - $lang->resource->workflowlayout->admin = 'admin'; - $lang->resource->workflowlayout->block = 'block'; + $lang->resource->workflowlayout->admin = 'admin'; + $lang->resource->workflowlayout->block = 'block'; + $lang->resource->workflowlayout->addUI = 'addUI'; + $lang->resource->workflowlayout->editUI = 'editUI'; + $lang->resource->workflowlayout->deleteUI = 'deleteUI'; + $lang->workflowlayout->methodOrder[5] = 'admin'; $lang->workflowlayout->methodOrder[10] = 'block'; + $lang->workflowlayout->methodOrder[15] = 'addUI'; + $lang->workflowlayout->methodOrder[20] = 'editUI'; + $lang->workflowlayout->methodOrder[25] = 'deleteUI'; /* workflowlinkage */ $lang->resource->workflowlinkage = new stdclass(); diff --git a/framework/api/entry.class.php b/framework/api/entry.class.php index 34fdece27e..cfe304c278 100644 --- a/framework/api/entry.class.php +++ b/framework/api/entry.class.php @@ -381,12 +381,13 @@ class baseEntry { $output = helper::removeUTF8Bom(ob_get_clean()); $output = json_decode((string) $output); - if(isset($output->data)) $output->data = json_decode((string) $output->data); - if(isset($output->load)) + if(isset($output->data) && !is_object($output->data)) $output->data = json_decode((string) $output->data); + if(!isset($output->status) && isset($output->result)) $output->status = $output->result; + if(isset($output->load->alert)) { $output->code = 400; $output->status = 'fail'; - $output->message = isset($output->load->alert) ? $output->load->alert : zget($output, 'confirm', ''); + $output->message = $output->load->alert; unset($output->load); } diff --git a/framework/base/router.class.php b/framework/base/router.class.php index 9618f019fe..96ad1a20b7 100644 --- a/framework/base/router.class.php +++ b/framework/base/router.class.php @@ -3294,7 +3294,7 @@ class baseRouter if(!empty($this->config->debug) && $this->config->debug > 1) { /* Send non-serious errors to page in zin mode. */ - $isZinRequest = isset($this->config->zin) || isset($_SERVER['HTTP_X_ZIN_OPTIONS']); + $isZinRequest = isset($_SERVER['HTTP_X_ZIN_OPTIONS']); $isNonSeriousError = $level !== E_ERROR && $level !== E_PARSE && $level !== E_CORE_ERROR && $level !== E_COMPILE_ERROR; if($isZinRequest && $isNonSeriousError) { diff --git a/framework/control.class.php b/framework/control.class.php index b562d0af42..cfb5425cdf 100644 --- a/framework/control.class.php +++ b/framework/control.class.php @@ -44,7 +44,6 @@ class control extends baseControl /* Code for task #9224. Set requiredFields for workflow. */ if($this->dbh && ($this->app->isServing() || (defined('RUN_MODE') and RUN_MODE == 'api'))) { - $this->extendRequireFields(); $this->extendExportFields(); $this->extendEditorFields(); @@ -495,7 +494,7 @@ class control extends baseControl { parse_str($extras, $result); $html = $this->flow->buildExtendHtmlValue($object, zget($result, 'position', 'info')); - $html .= $this->appendExtendCssAndJS($moduleName, $methodName); + $html .= $this->appendExtendCssAndJS($moduleName, $methodName, $object); } else { @@ -516,10 +515,11 @@ class control extends baseControl * @param zin\fieldList $fields * @param string $moduleName * @param string $methodName + * @param object $object * @access public * @return zin\fieldList */ - public function appendExtendFields(zin\fieldList $fields, string $moduleName = '', string $methodName = ''): zin\fieldList + public function appendExtendFields(zin\fieldList $fields, string $moduleName = '', string $methodName = '', object $object = null): zin\fieldList { if($this->config->edition == 'open') return $fields; if(!empty($this->app->installing) || !empty($this->app->upgrading)) return $fields; @@ -533,7 +533,8 @@ class control extends baseControl $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName); if(!$action || $action->extensionType != 'extend') return $fields; - $fieldList = $this->workflowaction->getFields($flow->module, $action->action); + $uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object); + $fieldList = $this->workflowaction->getFields($flow->module, $action->action, true, null, $uiID); return $this->loadModel('flow')->buildFormFields($fields, $fieldList); } @@ -543,10 +544,11 @@ class control extends baseControl * * @param string $moduleName * @param string $methodName + * @param object $object * @access public * @return string */ - public function appendExtendCssAndJS(string $moduleName = '', string $methodName = ''): string + public function appendExtendCssAndJS(string $moduleName = '', string $methodName = '', object $object = null): string { if($this->config->edition == 'open') return ''; if(!empty($this->app->installing) || !empty($this->app->upgrading)) return ''; @@ -560,7 +562,8 @@ class control extends baseControl $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName); if(!$action || $action->extensionType == 'none') return ''; - $fieldList = $this->workflowaction->getFields($flow->module, !empty($action->action) ? $action->action: ''); + $uiID = $this->loadModel('workflowlayout')->getUIByData($flow->module, !empty($action->action) ? $action->action: '', $object); + $fieldList = $this->loadModel('workflowaction')->getFields($flow->module, !empty($action->action) ? $action->action: '', true, null, $uiID); $html = ''; if(!empty($flow->css)) $html .= ""; @@ -593,8 +596,6 @@ class control extends baseControl $moduleName = $moduleName ? $moduleName : $this->app->rawModule; $methodName = $methodName ? $moduleName : $this->app->rawMethod; - if(!$object) $object = new stdclass(); - $this->loadModel('flow'); $this->loadModel('workflowfield'); @@ -604,9 +605,11 @@ class control extends baseControl $action = $this->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName); if(!$action || $action->extensionType != 'extend') return array(); + $uiID = is_object($object) ? $this->loadModel('workflowlayout')->getUIByData($flow->module, $action->action, $object) : 0; + $wrapControl = array('textarea', 'richtext', 'file'); - $fieldList = $this->workflowaction->getFields($flow->module, $action->action); - $layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName); + $fieldList = $this->workflowaction->getFields($flow->module, $action->action, true, $object, $uiID); + $layouts = $this->loadModel('workflowlayout')->getFields($moduleName, $methodName, $uiID); $notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty'); if($layouts) @@ -622,13 +625,13 @@ class control extends baseControl if($field->buildin || !$field->show || !isset($layouts[$field->field]) || (!empty($field->position) && $position != $field->position)) unset($fieldList[$key]); if((is_numeric($field->default) || $field->default) && empty($field->defaultValue)) $field->defaultValue = $field->default; - if(empty($object->{$field->field})) $object->{$field->field} = $field->defaultValue; + if($object && empty($object->{$field->field})) $object->{$field->field} = $field->defaultValue; if(in_array($field->type, $this->config->workflowfield->numberTypes)) $field = $this->workflowfield->processNumberField($field); $field->required = $field->readonly || ($notEmptyRule && strpos(",$field->rules,", ",{$notEmptyRule->id},") !== false); $field->control = $this->flow->buildFormControl($field); $field->items = $field->options ? array_filter($field->options) : null; - $field->value = !empty($object) ? zget($object, $field->field, '') : $defaultValue; + $field->value = !empty($object) ? zget($object, $field->field, '') : ''; $field->width = $field->width != 'auto' ? $field->width : 'full'; } @@ -684,13 +687,15 @@ class control extends baseControl * 将工作流扩展的必填字段加到requiredFields里,类似批量操作的会在checkFlow之前手动校验。 * append workflow extension fields to requiredFields. * + * @param int $objectID * @access public * @return void */ - public function extendRequireFields() + public function extendRequireFields(int $objectID = 0) { - $fields = $this->loadModel('workflowaction')->getFields($this->moduleName, $this->methodName); - $layouts = $this->loadModel('workflowlayout')->getFields($this->moduleName, $this->methodName); + $uiID = $this->loadModel('workflowlayout')->getUIByDataID($this->moduleName, $this->methodName, $objectID); + $fields = $this->loadModel('workflowaction')->getFields($this->moduleName, $this->methodName, true, null, $uiID); + $layouts = $this->loadModel('workflowlayout')->getFields($this->moduleName, $this->methodName, $uiID); $notEmptyRule = $this->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty'); foreach($fields as $field) { diff --git a/framework/model.class.php b/framework/model.class.php index 0b095519d8..dcd858c277 100644 --- a/framework/model.class.php +++ b/framework/model.class.php @@ -353,15 +353,16 @@ class model extends baseModel /** * Get flow extend fields. * + * @param int $objectID * @access public * @return array */ - public function getFlowExtendFields() + public function getFlowExtendFields(int $objectID = 0): array { if($this->config->edition == 'open') return array(); if(!empty($this->app->installing) || !empty($this->app->upgrading)) return array(); - return $this->loadModel('flow')->getExtendFields($this->app->getModuleName(), $this->app->getMethodName()); + return $this->loadModel('flow')->getExtendFields($this->app->getModuleName(), $this->app->getMethodName(), $objectID); } /** @@ -398,7 +399,8 @@ class model extends baseModel $this->loadModel('file'); if($this->post->uid) $this->file->updateObjectID($this->post->uid, $objectID, $moduleName); - $fields = $this->workflowaction->getFields($moduleName, $action->action, ''); + $uiID = $this->loadModel('workflowlayout')->getUIByDataID($moduleName, $methodName, $objectID); + $fields = $this->workflowaction->getFields($moduleName, $action->action, '', null, $uiID); foreach($fields as $field) { if($field->control == 'file' && $field->show && !$field->readonly) diff --git a/lib/date/date.class.php b/lib/date/date.class.php index 81a44a17d3..c0277c29af 100644 --- a/lib/date/date.class.php +++ b/lib/date/date.class.php @@ -43,7 +43,7 @@ class date */ public static function today() { - return date(DT_DATE2, time()); + return date(DT_DATE1, time()); } /** diff --git a/lib/form/form.class.php b/lib/form/form.class.php index 8da1aeac92..0bfb552609 100644 --- a/lib/form/form.class.php +++ b/lib/form/form.class.php @@ -65,15 +65,16 @@ class form extends fixer * Get the form data. * * @param array|null $configObject + * @param int $objectID * @return form */ - public static function data(array $configObject = null): form + public static function data(array $configObject = null, int $objectID = 0): form { global $app, $config; $form = new form; if($configObject === null) $configObject = $config->{$app->moduleName}->form->{$app->methodName}; - $configObject = $form->appendExtendFormConfig($configObject); + $configObject = $form->appendExtendFormConfig($configObject, '', '', $objectID); return $form->config($configObject); } @@ -102,10 +103,11 @@ class form extends fixer * @param array $config * @param string $moduleName * @param string $methodName + * @param int $objectID * @access public * @return array */ - public function appendExtendFormConfig(array $configObject, string $moduleName = '', string $methodName = ''): array + public function appendExtendFormConfig(array $configObject, string $moduleName = '', string $methodName = '', int $objectID = 0): array { global $app, $config; if($config->edition == 'open' || !empty($app->installing)) return $configObject; @@ -119,8 +121,9 @@ class form extends fixer $action = $app->control->loadModel('workflowaction')->getByModuleAndAction($flow->module, $methodName); if(!$action || $action->extensionType != 'extend') return $configObject; - $fieldList = $app->control->workflowaction->getFields($flow->module, $action->action); - $layouts = $app->control->loadModel('workflowlayout')->getFields($moduleName, $methodName); + $uiID = $app->control->loadModel('workflowlayout')->getUIByDataID($flow->module, $action->action, $objectID); + $fieldList = $app->control->workflowaction->getFields($flow->module, $action->action, true, null, $uiID); + $layouts = $app->control->workflowlayout->getFields($moduleName, $methodName, $uiID); $notEmptyRule = $app->control->loadModel('workflowrule')->getByTypeAndRule('system', 'notempty'); if($layouts) { @@ -195,6 +198,9 @@ class form extends fixer $module = $app->getModuleName() == 'story' ? $app->rawModule : $app->getModuleName(); $method = $app->getMethodName(); + if($module == 'feedback' && $app->rawMethod == 'tostory') $module = 'story'; + if($module == 'feedback' && $app->rawMethod == 'touserstory') $module = 'requirement'; + if($module == 'feedback' && $app->rawMethod == 'toepic') $module = 'epic'; if($method == 'batchcreate') $method = 'create'; if($method == 'batchedit') $method = 'edit'; diff --git a/lib/zin/func.php b/lib/zin/func.php index 4aeb431fba..ef0c7ba69d 100644 --- a/lib/zin/func.php +++ b/lib/zin/func.php @@ -208,6 +208,8 @@ function thinkCheckbox(): thinkCheckbox {return createWg('thinkCheckbox', func_g function thinkOptions(): thinkOptions{return createWg('thinkOptions', func_get_args());} function thinkTableInput(): thinkTableInput {return createWg('thinkTableInput', func_get_args());} function thinkInput(): thinkInput {return createWg('thinkInput', func_get_args());} +function thinkMatrixOptions(): thinkMatrixOptions {return createWg('thinkMatrixOptions', func_get_args());} +function thinkMulticolumn(): thinkMulticolumn {return createWg('thinkMulticolumn', func_get_args());} function thinkStepMenu(): thinkStepMenu {return createWg('thinkStepMenu', func_get_args());} function thinkResult(): thinkResult {return createWg('thinkResult', func_get_args());} function thinkSwot(): thinkSwot {return createWg('thinkSwot', func_get_args());} diff --git a/lib/zin/wg/backbtn/v1.php b/lib/zin/wg/backbtn/v1.php index b278d61a80..842bf0b571 100644 --- a/lib/zin/wg/backbtn/v1.php +++ b/lib/zin/wg/backbtn/v1.php @@ -44,8 +44,8 @@ class backBtn extends btn $backs = array( 'task' => 'execution-task,my-work,my-contribute,execution-tree,execution-grouptask,project-execution,product-track,repo-view,story-change,execution-kanban,execution-taskkanban,my-index,feedback-adminview,projectstory-track,execution-calendar,my-effort,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view', - 'story' => 'product-browse,projectstory-story,execution-story,my-work,my-contribute,productplan-view,build-view,projectbuild-view,product-track,repo-view,testcase-zerocase,my-index,feedback-adminview,projectstory-track,task-view,my-effort,demandpool-track,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view', - 'bug' => 'bug-browse,project-bug,my-work,my-contribute,execution-bug,bug-view,qa-index,execution-task,product-track,execution-task,task-view,repo-view,story-change,repo-review,feedback-adminview,my-index,projectstory-track,my-effort,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view', + 'story' => 'product-browse,projectstory-story,execution-story,my-work,my-contribute,productplan-view,build-view,projectbuild-view,product-track,repo-view,testcase-zerocase,my-index,feedback-adminview,projectstory-track,task-view,my-effort,demandpool-track,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view,ticket-browse,ticket-view', + 'bug' => 'bug-browse,project-bug,my-work,my-contribute,execution-bug,bug-view,qa-index,execution-task,product-track,execution-task,task-view,repo-view,story-change,repo-review,feedback-adminview,my-index,projectstory-track,my-effort,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view,ticket-browse,ticket-view', 'testcase' => 'testcase-browse,project-testcase,my-work,my-contribute,execution-testcase,testtask-cases,testsuite-view,product-browse,testcase-view,qa-index,caselib-browse,product-track,story-change,my-index,projectstory-track,my-effort,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view', 'testsuite' => 'testsuite-browse,testsuite-view,testtask-cases,my-index,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view', 'testtask' => 'testtask-browse,testtask-cases,qa-index,testcase-browse,execution-build,my-index,my-effort,product-dynamic,project-dynamic,execution-dynamic,project-view,execution-view', @@ -81,14 +81,16 @@ class backBtn extends btn 'risk' => 'risk-browse,risk-view,my-index,project-dynamic,execution-dynamic,project-view,execution-view', 'meeting' => 'meeting-browse,meeting-view,my-index,project-dynamic,execution-dynamic,project-view,execution-view', 'todo' => 'my-todo,my-effort', - 'ticket' => 'my-effort', + 'feedback' => 'my-effort,feedback-admin,feedback-browse,my-work,my-contribute', + 'ticket' => 'my-effort,ticket-browse,my-work,my-contribute', 'designguide' => 'designguide-browse,designguide-view,execution-task,task-view', // For designguide plugin. 'deploy' => 'deploy-browse,deploy-steps,deploy-scope,deploy-view,deploy-cases', 'service' => 'service-browse,service-manage,service-view', 'domain' => 'domain-browse,domain-view', 'researchtask' => 'marketresearch-task,my-index,product-dynamic,project-dynamic,project-view', 'review' => 'review-browse,my-index,product-dynamic,project-dynamic,project-view', - 'charter' => 'charter-browse' + 'charter' => 'charter-browse', + 'approvalflow' => 'approvalflow-browse' ); $props = parent::getProps(); diff --git a/lib/zin/wg/demandbasicinfo/v1.php b/lib/zin/wg/demandbasicinfo/v1.php index 61b0ac5e4e..a3ebeb7a5e 100644 --- a/lib/zin/wg/demandbasicinfo/v1.php +++ b/lib/zin/wg/demandbasicinfo/v1.php @@ -41,7 +41,7 @@ class demandBasicInfo extends wg zget($demands, $demand->parent), set::href(helper::createLink('demand', 'view', "demandID=$demand->parent")), set::title(zget($demands, $demand->parent)), - setClass('basis-32 text-clip mr-2.5'), + setClass('basis-52 text-clip mr-2.5'), setData('toggle', 'modal'), setData('size', 'lg') ), diff --git a/lib/zin/wg/detail/v1.php b/lib/zin/wg/detail/v1.php index a4e8d2c68b..84020b0d65 100644 --- a/lib/zin/wg/detail/v1.php +++ b/lib/zin/wg/detail/v1.php @@ -465,6 +465,7 @@ CSS; list($linkCreator, $prevBtn, $nextBtn, $objectType) = $this->prop(array('linkCreator', 'prevBtn', 'nextBtn', 'objectType')); $preAndNext = data('preAndNext'); + $idKey = isset($preAndNext->idKey) ? $preAndNext->idKey : 'id'; global $app; if(!$linkCreator && $preAndNext && ($prevBtn === true || $nextBtn === true)) @@ -473,9 +474,10 @@ CSS; } if($prevBtn === true && $preAndNext && $preAndNext->pre && $linkCreator) { - $prevBtn = array(); - $prevBtn['url'] = str_replace('{id}', "{$preAndNext->pre->id}", $linkCreator); - $prevBtn['hint'] = "#{$preAndNext->pre->id} " . (isset($preAndNext->pre->title) ? $preAndNext->pre->title : $preAndNext->pre->name); + $prevBtn = array(); + $objectID = $preAndNext->pre->$idKey; + $prevBtn['url'] = str_replace('{id}', "{$objectID}", $linkCreator); + $prevBtn['hint'] = "#{$objectID} " . (isset($preAndNext->pre->title) ? $preAndNext->pre->title : $preAndNext->pre->name); } elseif(is_string($prevBtn)) { @@ -483,9 +485,10 @@ CSS; } if($nextBtn === true && $preAndNext && $preAndNext->next && $linkCreator) { - $nextBtn = array(); - $nextBtn['url'] = str_replace('{id}', "{$preAndNext->next->id}", $linkCreator); - $nextBtn['hint'] = "#{$preAndNext->next->id} " . (isset($preAndNext->next->title) ? $preAndNext->next->title : $preAndNext->next->name); + $nextBtn = array(); + $objectID = $preAndNext->next->$idKey; + $nextBtn['url'] = str_replace('{id}', "{$objectID}", $linkCreator); + $nextBtn['hint'] = "#{$objectID} " . (isset($preAndNext->next->title) ? $preAndNext->next->title : $preAndNext->next->name); } elseif(is_string($nextBtn)) { @@ -540,7 +543,20 @@ CSS; $this->buildHeader(), $this->buildBody(), $layout === 'simple' ? null : $this->buildPrevAndNext(), - html($app->control->appendExtendCssAndJS()) + html($app->control->appendExtendCssAndJS('', '', $this->prop('object'))), + js + ( + 'const $doc = $(document)', + 'const binddedKey = "zt.detail.bindded"', + 'if($doc.data(binddedKey)) return', + '$doc.on("keyup.detail.zt", e => {', + 'let $btn = null', + 'if(e.keyCode === 37) $btn = $(".detail-prev-btn")', + 'else if(e.keyCode === 39) $btn = $(".detail-next-btn")', + 'if($btn && $btn.length) $btn[0].click()', + '}).data(binddedKey, true)', + '$doc.one("pageunmount.app", () => {$(document).off("keyup.detail.zt").removeData(binddedKey)})' + ) ); } } diff --git a/lib/zin/wg/detailbody/v1.php b/lib/zin/wg/detailbody/v1.php index a053949889..b88495f7d6 100644 --- a/lib/zin/wg/detailbody/v1.php +++ b/lib/zin/wg/detailbody/v1.php @@ -92,7 +92,7 @@ class detailBody extends wg empty($floating) ? null : center(setClass('pt-6 sticky bottom-0'), $floating) ), $side, - html($app->control->appendExtendCssAndJS()) + html($app->control->appendExtendCssAndJS('', '', data($app->getModuleName()))) ); } @@ -123,7 +123,7 @@ class detailBody extends wg $side ) ), - html($app->control->appendExtendCssAndJS()) + html($app->control->appendExtendCssAndJS('', '', data($app->getModuleName()))) ); } } diff --git a/lib/zin/wg/detailcard/v1.php b/lib/zin/wg/detailcard/v1.php index c62db59e34..d34451e1e7 100644 --- a/lib/zin/wg/detailcard/v1.php +++ b/lib/zin/wg/detailcard/v1.php @@ -60,9 +60,9 @@ class detailCard extends wg $objectID = $this->prop('objectID'); $object = $this->prop('object'); - if(!$objectType) $objectType = $app->rawModule; - if(!$object) $object = data($objectType); - if(!$objectID) $objectID = $object ? $object->id : data($objectType . 'ID'); + if(!$objectType) $objectType = $app->rawModule; + if(!$object) $object = data($objectType); + if(!$objectID) $objectID = $object ? $object->id : data($objectType . 'ID'); if(!$this->hasProp('objectType')) $this->setProp('objectType', $objectType); if(!$this->hasProp('objectID')) $this->setProp('objectID', $objectID); diff --git a/lib/zin/wg/featurebar/v1.php b/lib/zin/wg/featurebar/v1.php index 5c3db1eaef..76ffaa7d06 100644 --- a/lib/zin/wg/featurebar/v1.php +++ b/lib/zin/wg/featurebar/v1.php @@ -79,11 +79,13 @@ class featureBar extends wg foreach($moreSelects as $key => $text) { + $searchModule = data("config.{$currentModule}.search.module") ? data("config.{$currentModule}.search.module") : $currentModule; + $subItem = array(); $subItem['text'] = $text; $subItem['active'] = $key == $current; $subItem['url'] = ($callback instanceof \Closure) ? $callback($key, $text) : str_replace('{key}', (string)$key, $link); - $subItem['attrs'] = ['data-id' => $key, 'data-load' => $load, 'data-target' => $loadID, 'data-app' => $tab]; + $subItem['attrs'] = ['data-id' => $key, 'data-load' => $load, 'data-target' => $loadID, 'data-app' => $tab, 'data-success' => "() => zui.updateSearchForm('$searchModule')"]; if($item->name == 'QUERY') { diff --git a/lib/zin/wg/formpanel/v1.php b/lib/zin/wg/formpanel/v1.php index 5eeb766d93..5a792f7bfc 100644 --- a/lib/zin/wg/formpanel/v1.php +++ b/lib/zin/wg/formpanel/v1.php @@ -73,13 +73,21 @@ class formPanel extends panel return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js'); } + protected function getData() + { + global $app; + $moduleName = $app->getModuleName(); + if($moduleName == 'caselib') $moduleName = 'lib'; + return data($moduleName); + } + protected function created() { $fields = $this->prop('fields'); if(is_object($fields)) { global $app; - $fields = $app->control->appendExtendFields($fields); + $fields = $app->control->appendExtendFields($fields, '', '', $this->getData()); $this->setProp('fields', $fields); } @@ -171,9 +179,7 @@ class formPanel extends panel $layout = $this->prop('layout'); if($layout == 'grid') return null; - $moduleName = $app->getModuleName(); - if($moduleName == 'caselib') $moduleName = 'lib'; - $data = data($moduleName); + $data = $this->getData(); $fields = $app->control->appendExtendForm('info', $data); $extraMain = array(); foreach($fields as $field) @@ -206,7 +212,7 @@ class formPanel extends panel { global $app; - $data = data($app->getModuleName()); + $data = $this->getData(); $fields = $app->control->appendExtendForm('info', $data); $formBatchItem = array(); @@ -309,7 +315,7 @@ class formPanel extends panel setClass('panel-body ' . $this->prop('bodyClass')), set($this->prop('bodyProps')), $this->buildContainer($this->buildForm()), - html($app->control->appendExtendCssAndJS()) + html($app->control->appendExtendCssAndJS('', '', $this->getData())) ); } } diff --git a/lib/zin/wg/gantt/js/v1.js b/lib/zin/wg/gantt/js/v1.js index 28a95f435d..cc663446d7 100644 --- a/lib/zin/wg/gantt/js/v1.js +++ b/lib/zin/wg/gantt/js/v1.js @@ -483,11 +483,7 @@ function initGantt() gantt.templates.tooltip_text = function (start, end, task) { - if(task.type == 'point') return ''; - - let $text = $(task.text); - if($text.length == 0) return task.text; - if($text[2]) return $text[2].innerHTML; + return task.text; }; gantt.templates.task_end_date = function(data) @@ -590,7 +586,7 @@ function initGantt() if(editBtn.hasClass('icon-confirm')) { - let pointAttr = JSON.parse(reviewPoints); + let pointAttr = reviewPoints; let category = id.split("-")[1]; if(pointAttr[category]['disabled']) diff --git a/lib/zin/wg/header/v1.php b/lib/zin/wg/header/v1.php index 5d45f7e4b7..34428f214b 100644 --- a/lib/zin/wg/header/v1.php +++ b/lib/zin/wg/header/v1.php @@ -81,7 +81,7 @@ class header extends wg ); } - static function userBar() + public static function userBar() { global $lang, $app, $config; @@ -238,7 +238,6 @@ class header extends wg ), set::id('userMenu'), - set::trigger('hover'), set::placement('bottom-end'), set::menu(array('style' => array('color' => 'var(--color-fore)'))), set::strategy('fixed'), @@ -247,7 +246,7 @@ class header extends wg ); } - static function messageBar() + public static function messageBar() { global $app, $lang, $config; @@ -287,7 +286,7 @@ class header extends wg ); } - static function quickAddMenu() + public static function quickAddMenu() { global $app, $config, $lang; @@ -462,7 +461,6 @@ class header extends wg set::id('quickAddMenu'), set::menu(array('style' => array('color' => 'var(--color-fore)'))), - set::trigger('hover'), set::placement('bottom'), set::strategy('fixed'), set::arrow(true), diff --git a/lib/zin/wg/heading/v1.php b/lib/zin/wg/heading/v1.php index 687b776960..154ab20064 100644 --- a/lib/zin/wg/heading/v1.php +++ b/lib/zin/wg/heading/v1.php @@ -75,6 +75,7 @@ class heading extends wg if($app->tab == 'qa') $module = 'product'; if($app->tab == 'bi') $module = 'dimension'; + if($app->tab == 'feedback' && $moduleName == 'ticket') $module = 'product'; if($app->tab == 'qa' && ($moduleName == 'caselib' || data('isLibCase'))) $module = 'caselib'; return new dropmenu(set::tab($module)); } diff --git a/lib/zin/wg/mainnavbar/v1.php b/lib/zin/wg/mainnavbar/v1.php index 805d006c24..58a5bdd26f 100644 --- a/lib/zin/wg/mainnavbar/v1.php +++ b/lib/zin/wg/mainnavbar/v1.php @@ -138,7 +138,62 @@ class mainNavbar extends nav if($itemProps) $item = array_merge($item, $itemProps); if(is_callable($onRenderItem)) $item = $onRenderItem($item, $menuItem); - $items[] = $item; + if(isset($menuItem['dropMenu'])) + { + $dropItems = array(); + $label = $menuItem['text']; + $menuActive = ''; + foreach($menuItem['dropMenu'] as $dropMenuKey => $dropMenuItem) + { + if(isset($dropMenuItem['hidden']) && $dropMenuItem['hidden']) continue; + + if(empty($dropMenuItem['alias'])) $dropMenuItem['alias'] = ''; + if(empty($dropMenuItem['exclude'])) $dropMenuItem['exclude'] = ''; + + list($dropMenuName, $dropMenuModule, $dropMenuMethod, $dropMenuParams) = explode('|', $dropMenuItem['link']); + $dropActive = ''; + $dropModule = isset($dropMenuModule) ? $dropMenuModule : ''; + $dropMethod = isset($dropMenuMethod) ? $dropMenuMethod : ''; + $dropParams = isset($dropMenuParams) ? $dropMenuParams : ''; + $dropLabel = isset($dropMenuName) ? $dropMenuName : ''; + $dropLink = helper::createLink($dropModule, $dropMethod, $dropParams); + + $subModule = isset($dropMenuItem['subModule']) ? explode(',', $dropMenuItem['subModule']) : array(); + if($subModule && in_array($currentModule, $subModule)) $dropActive = 'active'; + if($dropModule == $currentModule && $dropMethod == $currentMethod) $dropActive = 'active'; + if($dropModule == $currentModule && strpos(",{$dropMenuItem['alias']},", ",{$currentMethod},") !== false) $dropActive = 'active'; + if(strpos(",{$dropMenuItem['exclude']},", ",{$currentModule}-{$currentMethod},") !== false || strpos(",{$dropMenuItem['exclude']},", ",{$currentModule},") !== false) $dropActive = ''; + if($dropActive) + { + $label = $dropLabel; + $menuActive = 'active'; + } + + $dropItems[] = array( + 'active' => $dropActive ? true : false, + 'data-id' => $dropMenuKey, + 'url' => $dropLink, + 'text' => $dropLabel, + 'data-app' => $app->tab + ); + } + + if(empty($dropItems)) continue; + $items[] = array + ( + 'type' => 'dropdown', + 'items' => $dropItems, + 'text' => $label, + 'data-id' => $name, + 'data-app' => $app->tab, + 'trigger' => 'hover', + 'class' => $menuActive + ); + } + else + { + $items[] = $item; + } } jsVar('allMainNavbarItems', $items); diff --git a/lib/zin/wg/modalheader/v1.php b/lib/zin/wg/modalheader/v1.php index 4702b3ed6d..5275676102 100644 --- a/lib/zin/wg/modalheader/v1.php +++ b/lib/zin/wg/modalheader/v1.php @@ -50,9 +50,10 @@ class modalHeader extends wg ($entityText || $entityID) ? entityLabel ( set::level(1), - setClass('pl-2'), + setClass('pl-2 flex'), $entityText ? set::text($entityText) : null, $entityID ? set::entityID($entityID) : null, + $entityID ? set::idClass('self-center') : null, set::reverse(true) ) : null, $this->block('suffix') diff --git a/lib/zin/wg/modulemenu/v1.php b/lib/zin/wg/modulemenu/v1.php index 075615c7a6..9c2f714c5f 100644 --- a/lib/zin/wg/modulemenu/v1.php +++ b/lib/zin/wg/modulemenu/v1.php @@ -91,7 +91,7 @@ class moduleMenu extends wg ); $items = $this->buildMenuTree($child->id); if($items) $item['items'] = $items; - if($child->id == $activeKey) + if($child->id == $activeKey || $child->id == 'product-' . $activeKey) { $itemKey = $this->prop('checkbox') ? 'checked' : 'selected'; $item[$itemKey] = true; @@ -129,7 +129,7 @@ class moduleMenu extends wg $modules = $this->prop('modules'); $moduleName = ''; - if($modules) array_map(function($module) use(&$moduleName, $activeKey) { if($module->id == $activeKey) $moduleName = $module->name; }, $modules); + if($modules) array_map(function($module) use(&$moduleName, $activeKey) { if($module->id == $activeKey || $module->id == 'product-' . $activeKey) $moduleName = $module->name; }, $modules); return $moduleName; } diff --git a/lib/zin/wg/monaco/js/v1.js b/lib/zin/wg/monaco/js/v1.js index a426b65a74..13e86a3829 100644 --- a/lib/zin/wg/monaco/js/v1.js +++ b/lib/zin/wg/monaco/js/v1.js @@ -24,7 +24,7 @@ function createMonaco(id, action, options, diffContent, onMouseDown, onMouseMove options.lineNumbers = function(number) { - var newlc = diffContent.line.new; + const newlc = diffContent.line.new; return newlc[number - 1]; }; @@ -32,13 +32,20 @@ function createMonaco(id, action, options, diffContent, onMouseDown, onMouseMove window.modifiedEditor = modifiedEditor; modifiedEditor.setModel({ - original: monaco.editor.createModel(diffContent.code.old.trim("\n"), options.lang), - modified: monaco.editor.createModel(diffContent.code.new.trim("\n"), options.lang), + original: monaco.editor.createModel(diffContent.code.old, options.lang), + modified: monaco.editor.createModel(diffContent.code.new, options.lang), }); editor = modifiedEditor.getModifiedEditor(); - var getOriginalEditor = modifiedEditor.getOriginalEditor(); + const getOriginalEditor = modifiedEditor.getOriginalEditor(); + getOriginalEditor.updateOptions({ + lineNumbers: function(number) + { + var oldlc = diffContent.line.old; + return oldlc[number - 1]; + } + }); if(onMouseDown) getOriginalEditor.onMouseDown(function(obj){eval(onMouseDown + '(obj)')}) if(onMouseMove) getOriginalEditor.onMouseMove(function(obj){eval(onMouseMove + '(obj)')}) diff --git a/lib/zin/wg/panel/v1.php b/lib/zin/wg/panel/v1.php index 3624fb8fcc..cecaa398a1 100644 --- a/lib/zin/wg/panel/v1.php +++ b/lib/zin/wg/panel/v1.php @@ -12,6 +12,7 @@ class panel extends wg 'size?: "sm"|"lg"', // 额外尺寸。 'title?: string', // 标题。 'shadow?: bool=true', // 阴影效果。 + 'titleIcon?: string', // 标题图标。 'titleClass?: string', // 标题类名。 'titleProps?: array', // 标题属性。 'headingClass?: string', // 标题栏类名。 diff --git a/lib/zin/wg/productsbox/v1.php b/lib/zin/wg/productsbox/v1.php index 75547cd9e5..b130a2abc2 100644 --- a/lib/zin/wg/productsbox/v1.php +++ b/lib/zin/wg/productsbox/v1.php @@ -190,6 +190,7 @@ class productsBox extends wg set::className('planBox'), picker ( + set::className('pr-2.5'), set::name("plans[{$planProductID}][]"), set::items(!empty($productPlans) ? $productPlans : array()), set::value($currentPlan), diff --git a/lib/zin/wg/searchtoggle/v1.php b/lib/zin/wg/searchtoggle/v1.php index f0e47e5f3a..0b52730d36 100644 --- a/lib/zin/wg/searchtoggle/v1.php +++ b/lib/zin/wg/searchtoggle/v1.php @@ -29,6 +29,8 @@ class searchToggle extends wg protected function build() { + if(!common::hasPriv('search', 'buildForm')) return; + global $lang, $app, $config; list($target, $module, $open, $url, $searchUrl, $text) = $this->prop(array('target', 'module', 'open', 'url', 'searchUrl', 'text')); diff --git a/lib/zin/wg/storybasicinfo/v1.php b/lib/zin/wg/storybasicinfo/v1.php index 503a4c6e71..f1bbb5cf48 100644 --- a/lib/zin/wg/storybasicinfo/v1.php +++ b/lib/zin/wg/storybasicinfo/v1.php @@ -107,7 +107,7 @@ class storyBasicInfo extends wg $demand->title, set::href(helper::createLink('demand', 'view', "demandID=$story->demand")), set::title($demand->title), - setClass('basis-32 text-clip mr-2.5'), + setClass('basis-52 text-clip mr-2.5'), setData('toggle', 'modal'), setData('size', 'lg') ) : $demand->title, @@ -142,7 +142,7 @@ class storyBasicInfo extends wg $story->parentName, set::href(helper::createLink($story->parentType, 'view', "storyID=$story->parent")), set::title($story->parentName), - setClass('basis-32 text-clip mr-2.5'), + setClass('basis-52 text-clip mr-2.5'), setData('toggle', 'modal'), setData('size', 'lg') ), @@ -208,7 +208,7 @@ class storyBasicInfo extends wg $items[$lang->story->source] = array ( 'control' => 'text', - 'content' => zget($lang->story->sourceList, $story->source, ''), + 'content' => zget($lang->{$story->type}->sourceList, $story->source, ''), 'id' => 'sourceBox' ); $items[$lang->story->sourceNote] = array @@ -230,12 +230,12 @@ class storyBasicInfo extends wg 'class' => 'stage-line', 'text' => zget($lang->{$story->type}->stageList, $this->getMinStage($story, $branches), '') ); - $items[$lang->story->category] = zget($lang->story->categoryList, $story->category); + $items[$lang->story->category] = zget($lang->{$story->type}->categoryList, $story->category); $items[$lang->story->pri] = array ( 'control' => 'pri', 'pri' => $story->pri, - 'text' => $lang->story->priList + 'text' => $lang->{$story->type}->priList ); $items[$lang->story->estimate] = $story->estimate . $config->hourUnit; if(in_array($story->source, $config->story->feedbackSource)) diff --git a/lib/zin/wg/thinkbasecheckbox/v1.php b/lib/zin/wg/thinkbasecheckbox/v1.php index fa8319294a..cbdc915cb0 100644 --- a/lib/zin/wg/thinkbasecheckbox/v1.php +++ b/lib/zin/wg/thinkbasecheckbox/v1.php @@ -41,6 +41,7 @@ class thinkBaseCheckbox extends wg global $lang; if($item instanceof item) $item = $item->props->toJSON(); + $disabled = $this->prop('disabled'); if(!isset($item['checked'])) { @@ -65,7 +66,7 @@ class thinkBaseCheckbox extends wg ( setClass('item-control has-input w-full py-2 px-3 flex gap-3 items-center justify-between border cursor-pointer ' . $itemClass), setData('type', $this->prop('type')), - on::click('toggleChecked'), + !$disabled ? on::click('toggleChecked') : null, div ( setClass('flex items-start text-md gap-1.5 flex-1'), @@ -95,7 +96,7 @@ class thinkBaseCheckbox extends wg return div ( setData('type', $this->prop('type')), - on::click('toggleChecked'), + !$disabled ? on::click('toggleChecked') : null, setClass('item-control w-full py-2 px-3 flex gap-3 items-center justify-between border cursor-pointer ' . $itemClass), div(setClass('text-md flex-1 break-all'), $text), new checkbox @@ -127,7 +128,7 @@ class thinkBaseCheckbox extends wg if(!is_array($item)) $item = array('text' => $item, 'value' => $key); if(!isset($item['checked'])) $item['checked'] = !empty($item['value']) && in_array($item['value'], $valueList); - $item['text'] = $prefix . '. ' . $item['text']; + $item['text'] = !empty($item['disabledPrefix']) ? $item['text'] : $prefix . '. ' . $item['text']; $items[$key] = $this->onBuildItem($item); } } diff --git a/lib/zin/wg/thinkcheckbox/v1.php b/lib/zin/wg/thinkcheckbox/v1.php index 465c114c94..3a1255d67f 100644 --- a/lib/zin/wg/thinkcheckbox/v1.php +++ b/lib/zin/wg/thinkcheckbox/v1.php @@ -22,12 +22,13 @@ class thinkCheckbox extends thinkRadio $app->loadLang('thinkstep'); $formItems = parent::buildFormItem(); - list($step, $minCount, $maxCount, $required) = $this->prop(array('step', 'minCount', 'maxCount', 'required')); + list($step, $minCount, $maxCount, $required, $setOption) = $this->prop(array('step', 'minCount', 'maxCount', 'required', 'setOption')); if($step) { - $required = $step->options->required; - $minCount = $step->options->minCount ?? null; - $maxCount = $step->options->maxCount ?? null; + $required = $step->options->required; + $minCount = $step->options->minCount ?? null; + $maxCount = $step->options->maxCount ?? null; + $setOption = !empty($step->options->setOption) ? $step->options->setOption : 0; } $className = 'selectable-rows' . (empty($required) ? ' hidden' : ''); @@ -38,7 +39,7 @@ class thinkCheckbox extends thinkRadio ( set::label($lang->thinkstep->label->minCount), set::labelClass('required'), - setClass($className), + setClass($className, 'min-count'), input ( set::placeholder($lang->thinkstep->placeholder->inputContent), @@ -46,20 +47,22 @@ class thinkCheckbox extends thinkRadio set::min(1), set::name('options[minCount]'), set::value($minCount), + set::disabled($setOption) ), ), formGroup ( set::label($lang->thinkstep->label->maxCount), set::labelClass('required'), - setClass($className), + setClass($className, 'max-count'), input ( - set::placeholder($lang->thinkstep->placeholder->inputContent), + set::placeholder($setOption ? $lang->thinkstep->placeholder->maxCount : $lang->thinkstep->placeholder->inputContent), set::type('number'), set::min(1), set::name('options[maxCount]'), - set::value($maxCount) + set::value($maxCount), + set::disabled($setOption) ) ) ); diff --git a/lib/zin/wg/thinkmatrixoptions/css/v1.css b/lib/zin/wg/thinkmatrixoptions/css/v1.css new file mode 100644 index 0000000000..0b12fd5644 --- /dev/null +++ b/lib/zin/wg/thinkmatrixoptions/css/v1.css @@ -0,0 +1,35 @@ +.think-multiple {--tw-ring-color: rgba(var(--color-gray-300-rgb), .7); box-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);} +.think-multiple-body::-webkit-scrollbar {height: 5px;} +.think-multiple-body::-webkit-scrollbar-track {background: var(--color-gray-200);} +.think-multiple-item {min-width: 126px; position: relative; display: flex; border-radius: var(--radius); align-items: stretch; min-height: 80px; --tw-ring-color: rgba(var(--color-gray-300-rgb), .7); box-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); margin-top: 1px; z-index: 1;} +.think-multiple-item.is-focus {z-index: 100;} +.think-multiple-action {opacity: 0; height: clac(8 * var(--space)); width: calc(6 * var(--space)); min-width: calc(6 * var(--space)); max-width: calc(6 * var(--space)); display: flex; justify-content: center; color: var(--color-gray-600);} +.think-multiple-item:hover .think-multiple-action {opacity: 1;} +.think-multiple-item-move {cursor: move; padding-top: 10px;} +.think-multiple-col-delete {padding-top: 4px;} +.think-multiple-action .icon {font-size: 12px;} +.think-multiple-item-text::placeholder {color: var(--color-gray-400);} +.think-multiple .think-multiple-action button {width: calc(5 * var(--space));} +.think-multiple .btn-action:hover {color: var(--color-primary-500);} +.think-multiple-item .form-control {background: inherit;} +.think-multiple-item {background: var(--color-canvas);} +.think-multiple-item[data-level="1"] {margin-top: 0;} +.think-multiple-drag-ghost {position: absolute; left: 0; top: 0; bottom: 0; opacity: 0; right: 0; pointer-events: none;} +.think-multiple-item-move:hover {color: var(--color-primary-500);} +.has-dragging .think-multiple-item-move > .icon-move {display: none;} +.think-multiple-item.move-hover {background-color: rgba(var(--color-primary-100-rgb),.2);} +.think-multiple-item.is-dragging {background-color: rgba(var(--color-primary-100-rgb),.4);} +.think-multiple-item.is-sub-dragging {background-color: rgba(var(--color-primary-100-rgb),.2);} +.think-multiple-item.is-dropping {z-index: 5;} +.think-multiple-item.is-dropping::before, +.think-multiple-item.is-dropping::after {content: ' '; display: block; position: absolute; top: 0; z-index: 110;} +.think-multiple-item.is-dropping::after {transform: translateX(-2px);} +.think-multiple-item.is-dropping::before {width: 2px; height: 100%; background: var(--color-primary-500); top: 0;} +.think-multiple-item.is-dropping[data-drop-side="left"]::before {left: 0;} +.think-multiple-item.is-dropping[data-drop-side="right"]::before {right: 0;} +.think-multiple-item.is-dropping[data-drop-side="left"]::after {left: 0;} +.think-multiple-item.is-dropping[data-drop-side="right"]::after {right: -4px;} +.think-multiple-item.is-dropping::after {width: 6px; height: 6px; border-radius: 50%; background: var(--color-primary-500); border: var(--color-primary-100) 3px solid; z-index: 120;} +.think-multiple-item.is-invalid-drop-level.is-dropping::before, +.think-multiple-item.is-invalid-drop-level.is-dropping::after {filter: grayscale(1); opacity: .5;} +.think-multiple .think-multiple-buttons { width: 100%; position: absolute; bottom: -20px; right: 0; text-align: right;} diff --git a/lib/zin/wg/thinkmatrixoptions/js/v1.js b/lib/zin/wg/thinkmatrixoptions/js/v1.js new file mode 100644 index 0000000000..8b6c4faff3 --- /dev/null +++ b/lib/zin/wg/thinkmatrixoptions/js/v1.js @@ -0,0 +1,409 @@ +function updateItemName(item, name) +{ + if(name === undefined) name = item.name; + if(item.infoName === name) return; + item.infoName = name; + item.name = name; + const namePath = name.split('.'); + item.order = namePath.reduce((total, level, index) => total + (+level * ([1000000, 1000, 1, 0.001, 0.000001][index])), 0); + item.level = namePath.length; + item.selfName = namePath.pop(); + item.parentName = namePath.join('.'); +} + +function updateChildrenName(item) +{ + if(!item.children) return; + item.children.forEach((subItem, subIndex) => + { + updateItemName(subItem, `${item.name}.${subIndex + 1}`); + updateChildrenName(subItem); + }); +} + +/** ThinkMatrixOptions component. */ +class ThinkMatrixOptions extends zui.Component +{ + static NAME = 'ThinkMatrixOptions'; + + static DEFAULT = + { + colName: 'colFields', + cols: ['', '', '', ''], + moveIcon: 'move', + deleteIcon: 'close', + deleteColTip: '', + addColTip: '', + changeLevelByDrag: false, + colPlaceholder: '', + addColText: '', + isSaveData: true, + }; + + init() + { + this.reset(this.options.cols, true); + } + + afterInit() + { + this.render(); + const $element = this.$element; + $element.on('focus', 'input', e => + { + const $input = $(e.target); + $input.closest('.think-multiple-item').addClass('is-focus'); + }) + .on('blur', 'input', e => + { + const $input = $(e.target); + $input.closest('.think-multiple-item').removeClass('is-focus'); + this.saveData(); + }) + .on('input', 'input', e => {this.saveData();}) + .on('click', '.btn-action', e => + { + const $btn = $(e.currentTarget); + if($btn.is('.disabled')) return; + + const action = $btn.attr('data-action'); + const name = $btn.closest('.think-multiple-item').attr('data-name'); + if(action === 'delete') this.deleteItem(name); + else if(action === 'sib') this.addSib(); + }).on('mouseenter', '.think-multiple-item-move', e => + { + $element.find('.move-hover').removeClass('move-hover'); + $(e.currentTarget).closest('.think-multiple-item').addClass('move-hover'); + }).on('mouseleave', '.think-multiple-item-move', e => + { + $element.find('.move-hover').removeClass('move-hover'); + }); + + $element.draggable( + { + selector: '.think-multiple-item', + handle: '.think-multiple-item-move', + beforeDrag: (_event, dragElement) => + { + const dragName = $(dragElement).attr('data-name'); + this.dragName = dragName; + this.dragItem = this.getByName(dragName); + this.dragMaxLevel = this.dragItem.level; + this.isValidLevel = true; + $element.find(`.think-multiple-item[data-name^="${dragName}."]`).addClass('is-sub-dragging').each((_index, ele) => + { + const level = +($(ele).attr('data-level')); + if(level > this.dragMaxLevel) this.dragMaxLevel = level; + }); + $element.removeClass('cursor-not-allowed'); + }, + onDragStart: (event, dragElement) => + { + event.dataTransfer.setDragImage($(dragElement).find('.think-multiple-drag-ghost')[0], 0, 0); + }, + onDragEnd: () => + { + $element.find('.is-sub-dragging').removeClass('is-sub-dragging'); + const dropName = this.dropName; + if(dropName) + { + this.dropName = null; + const $dropRow = this.getRow(dropName); + if($dropRow && $dropRow.length) $dropRow.removeAttr('data-drop-side').removeAttr('data-drop-level'); + } + + if(!this.isValidLevel) return; + const dragName = this.dragName; + if(!dropName || dragName === dropName) return; + if(this.dropSide === 'right') this.moveAfter(dragName, dropName); + else this.moveBefore(dragName, dropName); + this.saveData(true); + }, + onDragOver: (event, dragElement, dropElement) => + { + const $col = $(dropElement); + const dropName = $col.attr('data-name'); + let idDiff = false; + if(dropName !== this.dropName) + { + this.dropName = dropName; + const $oldRow = this.getRow(dropName); + $oldRow.removeAttr('data-drop-side') + .removeAttr('data-drop-level') + .removeAttr('data-invalid-nested'); + this.dropSide = ''; + this.dropLevel = ''; + this.isValidLevel = undefined; + idDiff = true; + } + const dropItem = this.getByName(dropName); + const dropBounding = dropElement.getBoundingClientRect(); + const dropSide = event.clientX > (dropBounding.left + dropBounding.width / 2) ? 'right' : 'left'; + const dropLevel = Math.max(1, Math.min(4, (!this.options.changeLevelByDrag || event.clientX <= (dropBounding.left + 24)) ? dropItem.level : (Math.round((event.clientX - dropBounding.left - 24 - 8) / 14)))); + const isValidLevel = (this.dragMaxLevel + (dropLevel - this.dragItem.level)) <= 4; + if(this.isValidLevel !== isValidLevel) + { + this.isValidLevel = isValidLevel; + $col.toggleClass('is-invalid-drop-level', !isValidLevel); + } + $element.toggleClass('cursor-not-allowed', !isValidLevel); + if(this.dropSide !== dropSide) + { + this.dropSide = dropSide; + $col.attr('data-drop-side', dropSide); + idDiff = true; + } + if(this.dropLevel !== dropLevel) + { + this.dropLevel = dropLevel; + $col.attr('data-drop-level', dropLevel); + idDiff = true; + } + }, + target: (dragElement) => + { + return $element.find('.think-multiple-item').not('.is-sub-dragging').not(dragElement); + } + }); + } + + saveData(isClear = false) + { + if(!this.options.isSaveData) return; + + const cols = []; + $('.think-multiple-body .think-multiple-item-text').each(function() + { + if($(this).val()) + { + const item = {value: $(this).closest('.think-multiple-item').data('name'), text: $(this).val()}; + cols.push(item); + } + }); + $('.required-options .picker-box').zui('picker').render({items: cols}); + if(isClear) $('.required-options .picker-box').zui('picker').$.setValue(''); + } + + getByID(id) + { + return this._items.find(x => x.id === id); + } + + getByName(name) + { + return this._items.find(x => x.name === name); + } + + getParent(item) + { + if(typeof item === 'string') item = this.getByName(item); + return (item && item.parentName) ? this.getByName(item.parentName) : null; + } + + getRow(name) + { + return this.$element.find(`.think-multiple-item[data-name="${name}"]`); + } + + reset(data, skipRender) + { + this._items = []; + this.update(data, skipRender); + } + + update(data, skipRender) + { + data.forEach(item =>{this.updateItem(item, true);}); + if(!skipRender) this.render(); + } + + updateItem(item, skipRender) + { + item = typeof item === 'string' ? {name: item, id: $.guid++, step: ''} : $.extend({id: $.guid++, step: ''}, item); + updateItemName(item); + + const index = this._items.findIndex(x => x.id === item.id); + if(index >= 0) + { + const oldItem = this._items[index]; + const parent = this.getParent(oldItem); + if(parent) parent.children.splice(parent.children.indexOf(oldItem), 1); + item = $.extend(oldItem, item); + this._items.splice(index, 1); + updateChildrenName(item); + } + if(item.level > 1) + { + this._items.push(item); + const parent = this._ensureItem(item.parentName); + const siblings = parent.children || []; + const oldIndex = siblings.findIndex(x => x.id === item.id); + if(oldIndex >= 0) siblings.splice(oldIndex, 1); + const index = siblings.findIndex(x => x.order > item.order); + if(index === 0) siblings.unshift(item); + else if(index < 0) siblings.push(item); + else if(index > 0) siblings.splice(index, 0, item); + parent.children = siblings; + } + else + { + const siblings = this._items; + const index = siblings.findIndex(x => x.level === 1 && x.order > item.order); + if(index === 0) siblings.unshift(item); + else if(index < 0) siblings.push(item); + else if(index > 0) siblings.splice(index, 0, item); + } + + if(!skipRender) this.render(); + return item; + } + + _ensureItem(name) + { + return this.getByName(name) || this.updateItem(name); + } + + _createCol(item, options) + { + const name = options.cols[item.index] || ''; + const $col = $ + ([ + `
`, + '
', + '
', + '
', + '
', + ``, + '
', + '
', + '
', + '
', + ``, + '
', + '
' + ].join('')); + return $col; + } + + _renderCol(item, $preCol, $list, $cols) + { + const options = this.options; + let $col = $cols.filter(`[data-id="${item.id}"]`); + if(!$col.length) $col = this._createCol(item, options); + + if($preCol) $col.insertAfter($preCol); + else $list.prepend($col); + + const hasSub = !!(item.children && item.children.length); + $col.attr('data-level', item.level) + .attr('data-name', item.name) + .attr('data-index', item.index) + .toggleClass('has-children', hasSub) + .toggleClass('no-child', !hasSub) + .removeClass('is-expired'); + $col.find('.think-multiple-item-text').attr({'name': `${options.colName}[${item.name}]`, placeholder: `${options.colPlaceholder}${item.name}`}); + $col.find('.think-multiple-col-delete .btn').toggleClass('disabled', hasSub || this._items.length == 1); + $col.find('.think-multiple-col-delete .btn.disabled').attr('title', options.deleteColTip); + $col.find('.think-multiple-item-move').toggleClass('disabled', this._items.length == 1); + return $col; + } + + render() + { + const items = this._items; + let rootIndex = 0; + items.forEach(item => + { + updateItemName(item); + if(item.level === 1) + { + item.index = rootIndex; + rootIndex++; + updateItemName(item, `${rootIndex}`); + } + if(item.children) + { + item.children.forEach((subItem, subIndex) => + { + subItem.index = subIndex; + updateItemName(subItem, `${item.name}.${subIndex + 1}`); + }); + } + }); + items.sort((a, b) => a.order - b.order); + + const $list = this.$element.find('.think-multiple-body'); + const $cols = $list.find('.think-multiple-item').addClass('is-expired'); + let $preCol = null; + items.forEach(item => + { + $preCol = this._renderCol(item, $preCol, $list, $cols); + }); + $cols.filter('.is-expired').remove(); + if(!this.$element.find('.think-multiple-buttons').length) this.$element.find('.think-multiple-body').append(`
`); + this.$element.find('.think-multiple-buttons .action-add').prop('disabled', items?.length && items.length > 15).attr('title', items?.length && items.length > 15 ? this.options.addColTip : ''); + } + + deleteItem(name) + { + const index = this._items.findIndex(x => x.name === name); + if (index < 0) return; + const item = this._items[index]; + this._items.splice(index, 1); + const parent = this.getParent(item); + if(parent) + { + const siblings = parent.children; + const index = siblings.indexOf(item); + if(index > -1) siblings.splice(index, 1); + } + if (!this._items.length) this.update(['1'], false); + const inputValue = this.$element.find(`.think-multiple-item[data-id="${item.id}"] .think-multiple-item-text`).val(); + this.render(); + this.saveData(!!inputValue); + } + + focus(name) + { + const $step = this.$element.find('.think-multiple-item[data-name="' + name + '"] .think-multiple-item-text'); + if($step.length) $step[0].focus(); + } + + addSib(focus = true) + { + if(this._items.length > 15) return; + + const fromName = this.$element.find('.think-multiple-item').last().attr('data-name'); + const item = this.getByName(fromName); + if(!item) return; + + const newItem = this.updateItem(item.name); + if(focus) this.focus(newItem.name); + this.$element.find(`.think-multiple-item[data-id="${newItem.id}"] .think-multiple-item-text`).val(''); + } + + moveAfter(fromName, toName) + { + if(fromName === toName) return; + const item = this.getByName(fromName); + if(!item) return; + item.name = toName; + this.updateItem(item); + } + + moveBefore(fromName, toName) + { + if(fromName === toName) return; + const item = this.getByName(fromName); + const toItem = this.getByName(toName); + if(!item || !toItem) return; + item.name = toItem.level > 1 ? `${toItem.parentName}.${+toItem.selfName - 1}` : `${+toItem.selfName - 1}`; + this.updateItem(item); + } +} + +/* Define $.fn.ThinkMatrixOptions() helper. */ +ThinkMatrixOptions.defineFn(); + +/* Extend ThinkMatrixOptions to zui object. */ +$.extend(zui, {ThinkMatrixOptions}); diff --git a/lib/zin/wg/thinkmatrixoptions/v1.php b/lib/zin/wg/thinkmatrixoptions/v1.php new file mode 100644 index 0000000000..4bc782b4b7 --- /dev/null +++ b/lib/zin/wg/thinkmatrixoptions/v1.php @@ -0,0 +1,63 @@ + + * @package zin + * @link http://www.zentao.net + */ +namespace zin; + +require_once dirname(__DIR__) . DS . 'input' . DS . 'v1.php'; + +class thinkMatrixOptions extends wg +{ + protected static array $defineProps = array + ( + 'id?: string="$GID"', // 组件根元素的 ID。 + 'colName?: string="colFields"', // 列数据的名称。 + 'cols?: array', // 列数据的默认值。 + 'deleteColTip?: string', // 禁用删除提示。 + 'addColTip?: string', // 禁用添加提示。 + 'addColText: string', // 添加列按钮文字。 + ); + + public static function getPageJS(): ?string + { + return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js'); + } + + public static function getPageCSS(): ?string + { + return file_get_contents(__DIR__ . DS . 'css' . DS . 'v1.css'); + } + + protected function build() + { + global $lang, $app; + $app->loadLang('thinkstep'); + $id = $this->prop('id') ? $this->prop('id') : $this->gid; + $deleteColTip = $this->prop('deleteColTip', $lang->thinkstep->tips->deleteCol); + $addColText = $this->prop('addColText', $lang->thinkstep->addCol); + $addColTip = $this->prop('addColTip', $lang->thinkstep->tips->addCol); + + return div + ( + setID($id), + setClass('think-multiple w-full'), + div(setClass('think-multiple-body flex overflow-x-auto overflow-y-hidden')), + zui::thinkMatrixOptions + ( + set::_to("#$id"), + set::deleteColTip($deleteColTip), + set::colPlaceholder($lang->thinkstep->label->columnTitle), + set::addColText($addColText), + set::addColTip($addColTip), + set($this->props->pick(array('colName', 'cols'))) + ) + ); + } +} diff --git a/lib/zin/wg/thinkmulticolumn/css/v1.css b/lib/zin/wg/thinkmulticolumn/css/v1.css new file mode 100644 index 0000000000..441f6a27ed --- /dev/null +++ b/lib/zin/wg/thinkmulticolumn/css/v1.css @@ -0,0 +1,8 @@ +.think-form-batch .form-batch-container::-webkit-scrollbar {height: 5px;} +.think-form-batch .form-batch-container::-webkit-scrollbar-track {background: var(--color-gray-200);} +.think-form-batch .form-batch-head .form-label {text-overflow: unset; white-space: unset; font-weight: 400; position: static; padding: 0;} +.think-form-batch .form-batch-table>*>tr>* {padding-top: calc(var(--space)); padding-bottom: calc(var(--space));} +.think-form-batch .form-batch-row-actions {padding-bottom: .5rem !important; padding-top: .5rem !important;} +.think-form-batch .form-batch-head[data-name="ACTIONS"], .think-form-batch .form-batch-row-actions {position: sticky; right: 0; background: var(--color-canvas);} +.think-form-batch .form-batch-head[data-name="ACTIONS"] .form-label {display: none;} +.think-form-batch .form-batch-row-actions .btn {color: var(--color-primary-500);} diff --git a/lib/zin/wg/thinkmulticolumn/js/v1.js b/lib/zin/wg/thinkmulticolumn/js/v1.js new file mode 100644 index 0000000000..941d3434f2 --- /dev/null +++ b/lib/zin/wg/thinkmulticolumn/js/v1.js @@ -0,0 +1,23 @@ +/** + * 处理输入框值变化。 + * Handling input box value changes. + * + * @access public + * @return void + */ +window.changeRows = function() +{ + const value = $(this).val(); + const intValue = value < 1 ? 1 : parseInt(value, 10); + $(this).val(intValue); +} + +window.renderRowData = function() +{ + $('.think-form-batch .form-batch-row-actions .btn[data-type="add"]').prop({ + disabled: $('.think-form-batch .form-batch-row-actions').length >= canAddRowsOfMulticol, + title: $('.think-form-batch .form-batch-row-actions').length >= canAddRowsOfMulticol ? addRowsTips.replace('%d', canAddRowsOfMulticol) : '', + }); +} + +$(document).on('click', '.think-form-batch .form-batch-row-actions .btn', renderRowData); diff --git a/lib/zin/wg/thinkmulticolumn/v1.php b/lib/zin/wg/thinkmulticolumn/v1.php new file mode 100644 index 0000000000..3183c57c55 --- /dev/null +++ b/lib/zin/wg/thinkmulticolumn/v1.php @@ -0,0 +1,169 @@ +prop('step'); + $key = $index + 1; + $requiredCols = isset($step->options->requiredCols) ? $step->options->requiredCols : array(); + return formBatchItem + ( + set::label($label), + set::name("result[col$key][]"), + set::width('110px'), + set::required(in_array($key, $requiredCols)) + ); + } + + protected function buildDetail(): array + { + global $lang; + $detailWg = parent::buildDetail(); + list($step, $fields, $canAddRows, $mode) = $this->prop(array('step', 'fields', 'canAddRows', 'mode')); + if($mode != 'detail') return array(); + + if($step) + { + $fields = $step->options->fields; + $canAddRows = $step->options->supportAdd == 1 ? $step->options->canAddRows : 0; + } + jsVar('canAddRowsOfMulticol', $canAddRows + 5); + jsVar('addRowsTips', $lang->thinkrun->tips->add); + + $batchItems = array(); + foreach($fields as $key => $field) + { + $batchItems[] = $this->buildFormBatchItem($field, $key); + } + $detailWg[] = formBatch + ( + setClass('think-form-batch'), + set::minRows(5), + set::actions(array()), + set::onRenderRow(jsRaw('renderRowData')), + $batchItems + ); + return $detailWg; + } + + protected function buildFormItem(): array + { + global $lang, $app; + $app->loadLang('thinkstep'); + $formItems = parent::buildFormItem(); + + list($step, $questionType, $required, $fields, $supportAdd, $canAddRows, $requiredCols) = $this->prop(array('step', 'questionType', 'required', 'fields', 'supportAdd', 'canAddRows', 'requiredCols')); + $requiredItems = $lang->thinkstep->requiredList; + + $requiredOptions = array(); + if($step) + { + $required = isset($step->options->required) ? $step->options->required : 1; + if(!empty($step->options->fields)) $step->options->fields = is_string($step->options->fields) ? explode(', ', $step->options->fields) : array_values((array)$step->options->fields); + $fields = $step->options->fields; + $requiredCols = $required && isset($step->options->requiredCols) ? $step->options->requiredCols : ''; + $supportAdd = $step->options->supportAdd; + $canAddRows = $supportAdd && isset($step->options->canAddRows) ? $step->options->canAddRows : ''; + foreach($fields as $key => $field) $requiredOptions[] = array('value' => $key + 1, 'text' => $field); + } + + $formItems[] = array( + formHidden('options[questionType]', $questionType), + formGroup + ( + set::label($lang->thinkstep->label->columnTitle), + setStyle(array('padding-bottom' => 'calc(4 * var(--space))')), + thinkMatrixOptions(set::colName('options[fields]'), set::cols($fields)) + ), + formRow + ( + setClass('mb-3'), + formGroup + ( + set::width('1/2'), + set::label($lang->thinkstep->label->required), + radioList + ( + set::name('options[required]'), + set::inline(true), + set::value($required), + set::items($requiredItems), + on::change()->toggleClass('.required-options', 'hidden', 'target.value == 0') + ) + ), + formGroup + ( + set::width('1/2'), + set::label($lang->thinkstep->label->requiredCol), + set::required(true), + setClass('required-options', $required ? '' : 'hidden'), + picker + ( + set::name('options[requiredCols][]'), + set::items($requiredOptions), + set::value($requiredCols), + set::multiple(true) + ) + ) + ), + formRow + ( + setClass('mb-3'), + formGroup + ( + setClass('w-1/2'), + set::label($lang->thinkstep->label->isSupportAdd), + set::labelHint($lang->thinkstep->tips->supportAdd), + radioList + ( + set::name('options[supportAdd]'), + set::inline(true), + set::items($lang->thinkstep->requiredList), + set::value(is_null($supportAdd) ? 1 : $supportAdd), + on::change()->toggleClass('.can-add-rows', 'hidden', 'target.value == 0') + ) + ), + formGroup + ( + setClass('w-1/2 can-add-rows', (is_null($supportAdd) || $supportAdd) ? '' : 'hidden'), + set::label($lang->thinkstep->label->canAddRows), + set::required(true), + input + ( + set::type('number'), + set::name('options[canAddRows]'), + set::value($canAddRows), + set::placeholder($lang->thinkstep->placeholder->inputContent), + set::min(1), + on::input('changeRows') + ) + ) + ) + ); + return $formItems; + } +} diff --git a/lib/zin/wg/thinkpestel/css/v1.css b/lib/zin/wg/thinkpestel/css/v1.css index 212bad68f5..173c128561 100644 --- a/lib/zin/wg/thinkpestel/css/v1.css +++ b/lib/zin/wg/thinkpestel/css/v1.css @@ -1,4 +1,4 @@ -.model-pestel .model-block::after {position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; font-size: 160px; font-weight: 600; pointer-events: none;} +.model-pestel .model-block::after {position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; font-size: 7.5rem; font-weight: 600; pointer-events: none;} .model-pestel .block-0 .model-block::after {content: 'P';} .model-pestel .block-1 .model-block::after {content: 'E';} .model-pestel .block-2 .model-block::after {content: 'S';} diff --git a/lib/zin/wg/thinkradio/js/v1.js b/lib/zin/wg/thinkradio/js/v1.js new file mode 100644 index 0000000000..c7abfcf097 --- /dev/null +++ b/lib/zin/wg/thinkradio/js/v1.js @@ -0,0 +1,57 @@ +/** + * 修改引用题目。 + * Change quote title. + * + * @param event + * @access public + * @return void + */ +window.changeQuoteTitle = function(event) +{ + let fields = []; // 所有的多列填空题的配置项 + let requiredFields = []; // 多列填空题必填的标题id + let citationItems = []; // 选择列picker组件的下拉项 + let isMulticolumn = false; + let multicolumnStep = false + + const quoteQuestions = $('.options-quote-title').data().quoteQuestions; + const quoteTitle = $('input[name="options[quoteTitle]').val(); + + $.each(quoteQuestions, function(index, item) { + const options = JSON.parse(item.options); + if(quoteTitle && item.id == quoteTitle && options.questionType === 'multicolumn') + { + multicolumnStep = true; + requiredFields = options.requiredCols; + fields = options.fields; + } + }); + + /* 渲染选择列的下拉项。Renders a drop-down item for a select column.*/ + $.each(requiredFields, function(index, key) { + /* 必填列的值-1是多列填空fields数组的key。The value of -1 in the required column is the key for the multi column fill in the blank fields array. */ + const keyValue = parseInt(key) - 1; + citationItems.push({key: keyValue, value:keyValue, text: fields[keyValue]}); + }) + + $('.quote-citation').toggleClass('gap-4', multicolumnStep); + $('.select-column').toggleClass('hidden', !multicolumnStep); + $('.citation').toggleClass('hidden', multicolumnStep); + $('.multicolumn-citation').toggleClass('hidden', !multicolumnStep); + if(multicolumnStep) + { + /* 引用题目为多列填空题的时候,重置选择列的下拉选项和默认值, 修改引用方式为:引用某行输入项。 */ + /* When the reference question is a multi-column fill-in-the-blank question, reset the drop-down options and default values of the selected column, and change the reference method to: reference an input item in a row. */ + $('.select-column .picker-box').zui('picker').render({items: citationItems}); + $('.select-column .picker-box').zui('picker').$.setValue(citationItems[0]); + $('.multicolumn-citation input[name="options[citation]"]').prop('checked', true); + } + else + { + /* 引用题目类型为多选或者单选题是,清空选择列的下拉选项和默认值,修改默认引用方式为:引用题的选中项。 */ + /* If the reference question type is multiple choice or single choice, clear the drop-down options and default values in the selection column, and change the default reference method to: reference the selected item in the question. */ + $('.select-column .picker-box').zui('picker').render({items: []}); + $('.select-column .picker-box').zui('picker').$.setValue(''); + if(citation) $('.citation input[id="options[citation]' + citation + '"]').prop('checked', true); + } +} diff --git a/lib/zin/wg/thinkradio/v1.php b/lib/zin/wg/thinkradio/v1.php index 0e01788655..d78122a07e 100644 --- a/lib/zin/wg/thinkradio/v1.php +++ b/lib/zin/wg/thinkradio/v1.php @@ -14,14 +14,25 @@ class thinkRadio extends thinkQuestion ( 'enableOther?: bool', 'fields?: array', + 'setOption?: bool=false', + 'quoteTitle?: string', + 'quoteQuestions?: array', + 'citation?: int=1', + 'selectColumn?: int', ); + public static function getPageJS(): ?string + { + return file_get_contents(__DIR__ . DS . 'js' . DS . 'v1.js'); + } + protected function buildDetail(): array { global $lang; $detailWg = parent::buildDetail(); - list($step, $mode) = $this->prop(array('step', 'mode')); + list($step, $mode, $isRun, $quoteQuestions) = $this->prop(array('step', 'mode', 'isRun', 'quoteQuestions')); if($mode != 'detail') return array(); + jsVar('citation', !empty($step->options->citation) ? $step->options->citation : 1); $answer = $step->answer; $result = isset($answer->result) ? $answer->result : array(); @@ -29,7 +40,9 @@ class thinkRadio extends thinkQuestion $fields = $step->options->fields ?? array(); $items = array(); - foreach($fields as $field) $items[] = array('text' => $field, 'value' => $field); + + if(!empty($fields)) foreach($fields as $field) $items[] = array('text' => $field, 'value' => $field); + if(empty($fields) && !$isRun) $items[] = array('text' => $lang->thinkstep->optionReference, 'disabledPrefix' => true); if(!empty($step->options->enableOther)) $items[] = array('text' => $lang->other, 'value' => 'other', 'isOther' => '1', 'other' => isset($answer->other) ? $answer->other : ''); $detailWg[] = thinkBaseCheckbox @@ -38,7 +51,24 @@ class thinkRadio extends thinkQuestion set::items($items), set::name('result[]'), set::value($step->options->questionType == 'radio' ? ($result[0] ?? '') : $result), + set::disabled($step->options->questionType == 'checkbox' && !empty($step->options->setOption) && $step->options->setOption == 1 && !$isRun) ); + $quoteItem = $step->options->questionType == 'checkbox' && !empty($step->options->setOption) && $step->options->setOption == 1; + $quoteName = ''; + + if($quoteItem && !empty($quoteQuestions)) + { + foreach($quoteQuestions as $item) + { + if(!$isRun && $item->id == $step->options->quoteTitle) $quoteName = $item->index . '. ' . $item->title; + if($isRun && $item->origin == $step->options->quoteTitle) $quoteName = $item->title; + } + $detailWg[] = div + ( + setClass('mt-3'), + sprintf($lang->thinkstep->tips->checkbox, $quoteName) + ); + } return $detailWg; } @@ -46,22 +76,148 @@ class thinkRadio extends thinkQuestion { global $lang, $app; $app->loadLang('thinkstep'); - $formItems = parent::buildFormItem(); - list($step, $questionType, $required, $enableOther, $fields) = $this->prop(array('step', 'questionType', 'required', 'enableOther', 'fields')); + $formItems = parent::buildFormItem(); + list($step, $questionType, $required, $enableOther, $fields, $setOption, $quoteTitle, $quoteQuestions, $citation, $selectColumn) = $this->prop(array('step', 'questionType', 'required', 'enableOther', 'fields', 'setOption', 'quoteTitle', 'quoteQuestions', 'citation', 'selectColumn')); + $requiredItems = $lang->thinkstep->requiredList; if($step) { - $enableOther = $step->options->enableOther ?? 0; - $required = $step->options->required; + $enableOther = $step->options->enableOther ?? 0; + $required = $step->options->required; + $setOption = isset($step->options->setOption) ? $step->options->setOption : false; + $defaultQuote = !empty($quoteQuestions) ? $quoteQuestions[0]->id : null; + $quoteTitle = isset($step->options->quoteTitle) ? $step->options->quoteTitle : $defaultQuote; + $citation = isset($step->options->citation) ? $step->options->citation : 1; + $selectColumn = isset($step->options->selectColumn) ? $step->options->selectColumn : null; if(!empty($step->options->fields)) $step->options->fields = is_string($step->options->fields) ? explode(', ', $step->options->fields) : array_values((array)$step->options->fields); - $fields = $step->options->fields ?? array(); + $fields = !empty($step->options->fields) ? $step->options->fields : array('', '', ''); + } + + $quoteQuestionsItems = array(); + if(!empty($quoteQuestions)) + { + foreach($quoteQuestions as $item) + { + $quoteQuestionsItems[] = array('text' => $item->title, 'value' => $item->id); + } } $formItems[] = array( formHidden('options[questionType]', $questionType), + $questionType == 'checkbox' ? array + ( + formRow + ( + formGroup + ( + setClass('w-66'), + set::label($lang->thinkstep->label->setOption), + radioList + ( + set::name('options[setOption]'), + set::inline(true), + set::value($setOption), + set::items($lang->thinkstep->setOptionList), + on::change() + ->const('maxCountPlaceholder', $lang->thinkstep->placeholder->maxCount) + ->const('inputContent', $lang->thinkstep->placeholder->inputContent) + ->do(" + $('.think-options-field').toggleClass('hidden', target.value == 1); + $('.think-quote').toggleClass('hidden', target.value == 0); + if(target.value == 1) + { + $('.min-count input').val(1).attr('disabled', 'disabled'); + $('.max-count input').val('').attr('placeholder', maxCountPlaceholder).attr('disabled', 'disabled'); + } + else + { + $('.min-count input').val('').removeAttr('disabled'); + $('.max-count input').attr('placeholder', inputContent).removeAttr('disabled'); + } + $('.text-danger').remove(); + $('.has-error').removeClass('has-error'); + ") + ) + ), + icon + ( + setClass('mt-9 text-gray-400 cursor-pointer ml-1 text-base pt-0.5'), + toggle::tooltip(array('placement' => 'top', 'title' => $lang->thinkstep->tips->setOption, 'width' => '220px', 'className' => 'bg-white text-gray border border-gray-300')), + 'help' + ) + ), + formGroup + ( + setClass('think-quote', $setOption == 0 ? 'hidden' : ''), + set::label($lang->thinkstep->label->quoteTitle), + set::labelClass('required'), + picker + ( + setdata('quote-questions', $quoteQuestions), + set(array + ( + 'class' => 'options-quote-title', + 'name' => 'options[quoteTitle]', + 'placeholder' => $lang->thinkstep->placeholder->quoteTitle, + 'items' => $quoteQuestionsItems, + 'value' => !empty($quoteTitle) && !empty($quoteQuestionsItems) ? $quoteTitle : '', + 'disabled' => empty($quoteQuestions), + 'title' => empty($quoteQuestions) ? $lang->thinkstep->tips->quoteTitle : null, + 'required' => true, + )), + bind::change('changeQuoteTitle(event)') + ) + ), + formRow + ( + setClass('think-quote quote-citation', $setOption == 0 ? 'hidden' : ''), + formGroup + ( + setClass('citation'), + set::label($lang->thinkstep->label->citation), + set::labelClass('required'), + radioList + ( + set::name('options[citation]'), + set::inline(true), + set::value($citation), + set::items($lang->thinkstep->citationList) + ) + ), + formGroup + ( + setClass('multicolumn-citation w-1/2', $citation != 3 ? 'hidden' : ''), + set::label($lang->thinkstep->label->citation), + set::labelClass('required'), + radioList + ( + set::name('options[citation]'), + set::inline(true), + set::value($citation), + set::items($lang->thinkstep->multiCitationList) + ) + ), + formGroup + ( + setClass('select-column', $citation != 3 ? 'hidden' : ''), + set::label($lang->thinkstep->label->selectColumn), + set::labelClass('required'), + picker( + set(array( + 'name' => 'options[selectColumn]', + 'placeholder' => $lang->thinkstep->placeholder->quoteTitle, + 'required' => true, + 'items' => array(), + 'value' => $selectColumn, + )) + ) + ) + ) + ) : null, formGroup ( + setClass('think-options-field', ($questionType === 'checkbox' && $setOption == 1) ? 'hidden' : ''), set::label($lang->thinkstep->label->option), thinkOptions ( diff --git a/lib/zin/wg/thinkresult/v1.php b/lib/zin/wg/thinkresult/v1.php index 9bd527c110..00a641d8dd 100644 --- a/lib/zin/wg/thinkresult/v1.php +++ b/lib/zin/wg/thinkresult/v1.php @@ -26,7 +26,7 @@ class thinkResult extends wg list($wizard, $mode, $blocks) = $this->prop(array('wizard', 'mode', 'blocks')); $model = $wizard->model; - $wgMap = array('swot' => 'thinkSwot', 'pffa' => 'thinkPffa', 'pest' => 'thinkPestel', '4p' => 'think4p'); + $wgMap = array('swot' => 'thinkSwot', 'pffa' => 'thinkPffa', 'pest' => 'thinkPestel', 'pestel' => 'thinkPestel', '4p' => 'think4p', '4p2' => 'think4p'); if(!isset($wgMap[$model])) return array(); return createWg($wgMap[$model], array(set::mode($mode), set::blocks($blocks))); diff --git a/lib/zin/wg/thinkstep/v1.php b/lib/zin/wg/thinkstep/v1.php index 8a8e4d9881..e7733a36b5 100644 --- a/lib/zin/wg/thinkstep/v1.php +++ b/lib/zin/wg/thinkstep/v1.php @@ -9,11 +9,12 @@ class thinkStep extends wg 'action?: string="detail"', 'addType?: string', 'isRun?: bool=false', // 是否是分析活动 + 'quoteQuestions?: array', // 引用题目的下拉选项 ); protected function buildBody(): wg|array { - list($item, $action, $addType, $isRun) = $this->prop(array('item', 'action', 'addType', 'isRun')); + list($item, $action, $addType, $isRun, $quoteQuestions) = $this->prop(array('item', 'action', 'addType', 'isRun', 'quoteQuestions')); $step = $addType ? null : $item; $questionType = $addType ? $addType : ($item->options->questionType ?? ''); @@ -21,8 +22,9 @@ class thinkStep extends wg if($addType === 'transition' || !$addType && $item->type === 'transition') return thinkTransition(set::step($step), set::mode($action), set::isRun($isRun)); if($questionType === 'input') return thinkInput(set::step($step), set::questionType('input'), set::mode($action), set::isRun($isRun)); if($questionType === 'radio') return thinkRadio(set::step($step), set::questionType('radio'), set::mode($action), set::isRun($isRun)); - if($questionType === 'checkbox') return thinkCheckbox(set::step($step), set::questionType('checkbox'), set::mode($action), set::isRun($isRun)); + if($questionType === 'checkbox') return thinkCheckbox(set::step($step), set::questionType('checkbox'), set::mode($action), set::isRun($isRun), set::quoteQuestions($quoteQuestions)); if($questionType === 'tableInput') return thinkTableInput(set::step($step), set::questionType('tableInput'), set::mode($action), set::isRun($isRun)); + if($questionType === 'multicolumn') return thinkMulticolumn(set::step($step), set::questionType('multicolumn'), set::mode($action), set::isRun($isRun)); return array(); } diff --git a/lib/zin/wg/thinkstepbase/v1.php b/lib/zin/wg/thinkstepbase/v1.php index c23f8ad106..420f178322 100644 --- a/lib/zin/wg/thinkstepbase/v1.php +++ b/lib/zin/wg/thinkstepbase/v1.php @@ -45,7 +45,7 @@ class thinkStepBase extends wg { $questionType = $options->questionType; $tips = $lang->thinkstep->$questionType; - if($options->required && $questionType == 'checkbox') + if($options->required && $questionType == 'checkbox' && $options->setOption == 0) { $tips = $lang->thinkrun->requiredTitle[$questionType]; $tips = str_replace(array('%min%', '%max%'), array($options->minCount, $options->maxCount), $tips); @@ -75,6 +75,7 @@ class thinkStepBase extends wg ( setClass('h-full text-fore text-lg'), $requiredSymbal, + isset($step->index) ? $step->index . '. ' : '', $step->title, $questionTips, $errorText diff --git a/lib/zin/wg/thinkstepmenu/v1.php b/lib/zin/wg/thinkstepmenu/v1.php index 58bbfa6426..cf3b1c650e 100644 --- a/lib/zin/wg/thinkstepmenu/v1.php +++ b/lib/zin/wg/thinkstepmenu/v1.php @@ -53,7 +53,7 @@ class thinkStepMenu extends wg $unClickable = $toggleNonNodeShow && $setting->id != $activeKey && $setting->type != 'node' && json_decode($setting->answer) == null; $item = array( 'key' => $setting->id, - 'text' => $setting->title, + 'text' => (isset($setting->index) ? ($setting->index . '. ') : '') . $setting->title, 'hint' => $unClickable ? $this->lang->thinkrun->error->unanswered :$setting->title, 'url' => $unClickable || !$canView ? '' : $setting->url, 'data-id' => $setting->id, @@ -188,34 +188,25 @@ class thinkStepMenu extends wg if($canCreate && (($showQuestionOfNode && $item->type == 'node') || $item->hasSameQuestion || $item->type == 'question')) $menus = array_merge($menus, array( array('type' => 'divider'), - array( - 'key' => 'radio', - 'icon' => 'radio', - 'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['radio'], - 'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'radio')"), - ), - array( - 'key' => 'checkbox', - 'icon' => 'checkbox', - 'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['checkbox'], - 'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'checkbox')"), - ), - array( - 'key' => 'input', - 'icon' => 'input', - 'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['input'], - 'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'input')"), - ), - array( - 'key' => 'tableInput', - 'icon' => 'cell-input', - 'text' => $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['tableInput'], - 'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', 'tableInput')"), - ), + $this->buildMenuItem('radio', 'radio', $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['radio'], $item, $parentID, 'radio'), + $this->buildMenuItem('checkbox', 'checkbox', $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['checkbox'], $item, $parentID, 'checkbox'), + $this->buildMenuItem('input', 'input', $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['input'], $item, $parentID, 'input'), + $this->buildMenuItem('tableInput', 'cell-input', $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['tableInput'], $item, $parentID, 'tableInput'), + $this->buildMenuItem('multicolumn', 'multi-input', $this->lang->thinkstep->createStep . $this->lang->thinkstep->actions['multicolumn'], $item, $parentID, 'multicolumn'), )); return $menus; } + private function buildMenuItem(string $key, string $icon, string $text, object $item, int $parentID): array + { + return array( + 'key' => $key, + 'icon' => $icon, + 'text' => $text, + 'onClick' => jsRaw("() => addQuestion({$item->id}, {$parentID}, 'question', '{$key}')"), + ); + } + private function buildActions(): node { return btn @@ -238,7 +229,7 @@ class thinkStepMenu extends wg ( div ( - setClass('think-node-menu rounded bg-white col bg-canvas pb-3 h-full'), + setClass('think-node-menu rounded bg-white col bg-canvas pb-3 h-full no-morph'), zui::$treeType ( set::_id('thinkNodeMenu'), diff --git a/lib/zin/wg/thinkswot/css/v1.css b/lib/zin/wg/thinkswot/css/v1.css index d588b92774..e9203093b7 100644 --- a/lib/zin/wg/thinkswot/css/v1.css +++ b/lib/zin/wg/thinkswot/css/v1.css @@ -1,6 +1,6 @@ .model-swot .block-0, .model-swot .block-1 {border-bottom: none;} .model-swot .model-block:nth-child(odd) {border-right: none;} -.model-swot .model-block::after {position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; font-size: 160px; font-weight: 600; pointer-events: none;} +.model-swot .model-block::after {position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #fff; font-size: 9rem; font-weight: 600; pointer-events: none;} .model-swot .model-block.block-0::after {content: 'S';} .model-swot .model-block.block-1::after {content: 'W';} .model-swot .model-block.block-2::after {content: 'O';} diff --git a/lib/zin/wg/thumbnail/v1.php b/lib/zin/wg/thumbnail/v1.php index 8d6d694425..61f9eb090a 100644 --- a/lib/zin/wg/thumbnail/v1.php +++ b/lib/zin/wg/thumbnail/v1.php @@ -45,12 +45,12 @@ class thumbnail extends wg ( div ( - setClass('flex items-center justify-center cursor-pointer bg-gray-100 w-full h-60'), + setClass('flex items-center justify-center cursor-pointer bg-white w-full h-60 '), setData(array('on' => 'click', 'call' => 'uploadThumbnail')), img ( setID('thumbnail-img'), - setClass('w-full h-full' . ($src ? '' : ' hidden')), + setClass('h-full' . ($src ? '' : ' hidden')), set::src($src), set::title($tips) ), diff --git a/lib/zin/wg/treeeditor/v1.php b/lib/zin/wg/treeeditor/v1.php index e5d7a0463b..00172ed8f5 100644 --- a/lib/zin/wg/treeeditor/v1.php +++ b/lib/zin/wg/treeeditor/v1.php @@ -90,8 +90,8 @@ class treeEditor extends wg $item['actions'] = array(); $item['actions']['items'] = array(); - if($canEdit) $item['actions']['items'][] = array('key' => 'edit', 'icon' => 'edit', 'data-toggle' => 'modal', 'url' => createLink('tree', 'edit', 'moduleID=' . $item['id'] . '&type=' . ($viewType ? $viewType : $item['type']))); - if($itemCanSplit) $item['actions']['items'][] = array('key' => 'view', 'icon' => 'split', 'url' => $item['url'], 'data-app' => $app->tab); + if($canEdit && $editType != 'task') $item['actions']['items'][] = array('key' => 'edit', 'icon' => 'edit', 'data-toggle' => 'modal', 'url' => createLink('tree', 'edit', 'moduleID=' . $item['id'] . '&type=' . ($viewType ? $viewType : $item['type']))); + if($itemCanSplit) $item['actions']['items'][] = array('key' => 'view', 'icon' => 'split', 'url' => $item['url'], 'data-app' => $app->tab); } elseif(isset($item['type']) && $item['type'] != 'branch') { diff --git a/lib/zin/wg/twinsstory/js/v1.js b/lib/zin/wg/twinsstory/js/v1.js index ea6c87b91f..dae1c6c92e 100644 --- a/lib/zin/wg/twinsstory/js/v1.js +++ b/lib/zin/wg/twinsstory/js/v1.js @@ -107,7 +107,7 @@ function loadPlanForTwins(productID, branch, branchIndex) { /* Load plan */ if(branch == '0') branch = ''; - planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '&planID=0&fieldID=' + branchIndex + '&needCreate=false&expi red=unexpired¶m=skipParent,forStory,' + config.currentMethod); + planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '¶ms=unexpired,noclosed&skipParent=true'); if(branchIndex > 0) { var $planIdBox = $('.addBranchesBox'+ branchIndex +' #planIdBox'); diff --git a/misc/ci/Jenkinsfile b/misc/ci/Jenkinsfile index 99adfb15dc..5f971fdf63 100644 --- a/misc/ci/Jenkinsfile +++ b/misc/ci/Jenkinsfile @@ -224,38 +224,11 @@ pipeline { // 仅制作开源版时不上传收费版相关源码 if (params.publish_exts!="open") { buildPkg.uploadSource("php8.1", ["php7.0", "php7.1", "php7.2_7.4"]) - env.PLUGIN_SAFE_EXIST = sh(returnStatus: true, script: 'test -d $SRC_ZENTAOEXT_PATH/zentaopro/safe') } } } } - stage("SAFe") { - when { - allOf { - environment name: 'PLUGIN_SAFE_EXIST', value: '0' - } - } - steps { - container('package') { - sh 'cd $SRC_ZENTAOEXT_PATH && make safe' - sh 'cp ${ZENTAO_BUILD_PATH}/safe.zip ${ZENTAO_BUILD_PATH}/safe-php8.1.zip' - sh 'cp ${ZENTAO_BUILD_PATH}/safe.zip ${ZENTAO_RELEASE_PATH}/safe-php8.1.zip' - sh '''${ZENTAO_BUILD_PATH}/script/downgrade/dirDowngrade.sh -n safe -s -i \ - -p 7.2,7.1,7.0 \ - -c ${PWD}/misc -r $SRC_ZENTAOEXT_PATH/zentaopro -o ${ZENTAO_BUILD_PATH} \ - safe ''' - sh 'cp -v ${ZENTAO_BUILD_PATH}/safe-*.zip ${ZENTAO_RELEASE_PATH}/' - sh 'cd $SRC_ZENTAOEXT_PATH && make encrypt-safe-all' - } - script { - env.UPLOADED_PLUGIN_SAFE = 'true' - buildPkg.uploadSAFe(["php7.0", "php7.1", "php7.2_7.4", "php8.1"]) - } - - } - } - } } // End Build } @@ -589,7 +562,7 @@ pipeline { sh 'make test-biz-k8s' sh 'make build-biz-k8s' } - + if (env.PUBLISH_MAX=="true") { sh 'make test-max' @@ -598,7 +571,7 @@ pipeline { sh 'make test-max-k8s' sh 'make build-max-k8s' } - + if (env.PUBLISH_IPD=="true") { sh 'make test-ipd' diff --git a/module/admin/control.php b/module/admin/control.php index 560aaf7625..dc2f65192d 100755 --- a/module/admin/control.php +++ b/module/admin/control.php @@ -248,6 +248,7 @@ class admin extends control public function setModule() { $this->loadModel('setting'); + $this->loadModel('project'); if($_POST) { @@ -266,6 +267,14 @@ class admin extends control $URAndSR = $this->config->edition == 'ipd' ? 1 : zget($data->module, 'productUR', 0); if($enableER && !$URAndSR) $this->send(array('result' => 'fail', 'message' => $this->lang->admin->notice->openUR)); + foreach($closedFeatures as $closedFeature) + { + if(in_array($closedFeature, array('productER', 'productUR'))) + { + $this->project->unlinkStoryByType(0, $closedFeature == 'productER' ? 'epic' : 'requirement'); + } + } + $this->setting->setItem('system.common.closedFeatures', implode(',', $closedFeatures)); $this->setting->setItem('system.common.global.scoreStatus', zget($data->module, 'myScore', 0)); $this->setting->setItem('system.custom.enableER', $enableER); diff --git a/module/admin/js/setmodule.ui.js b/module/admin/js/setmodule.ui.js index 3f4bd578a6..d62d87223d 100644 --- a/module/admin/js/setmodule.ui.js +++ b/module/admin/js/setmodule.ui.js @@ -7,6 +7,26 @@ function changeModule($target) } else { + if(name == 'module[productUR]' || name == 'module[productER]') + { + zui.Modal.confirm( + { + message: confirmDisableStoryType, + icon: 'icon-exclamation-sign', + iconClass: 'warning-pale rounded-full icon-2x' + }).then((res) => + { + if(res) + { + $("input[type=hidden][name='" + name + "']").val('0').removeAttr('disabled'); + return false; + } + + $target.prop('checked', true); + }); + return false; + } + $("input[type=hidden][name='" + name + "']").val('0').removeAttr('disabled'); } }; diff --git a/module/admin/model.php b/module/admin/model.php index 97075b249d..e4b59a1945 100755 --- a/module/admin/model.php +++ b/module/admin/model.php @@ -188,7 +188,7 @@ class adminModel extends model if($menuKey == 'company' && $this->app->rawModule != 'convert') { - $dept = $this->dao->select('id')->from(TABLE_DEPT)->fetch(); + $dept = $this->dao->select('id')->from(TABLE_DEPT)->limit(1)->fetch(); if($dept && common::hasPriv('company', 'browse')) $menu['link'] = helper::createLink('company', 'browse'); } @@ -428,11 +428,9 @@ class adminModel extends model */ public function genDateUsed(): object { - $firstUseDate = $this->dao->select('date')->from(TABLE_ACTION) - ->where('date')->gt('1970-01-01') + $firstUseDate = $this->dao->select('min(`date`) as `date`')->from(TABLE_ACTION) + ->where('`date`')->gt('1970-01-01') ->andWhere('actor')->eq($this->app->user->account) - ->orderBy('date_asc') - ->limit('1') ->fetch('date'); if($firstUseDate) $firstUseDate = substr($firstUseDate, 0, 10); diff --git a/module/admin/ui/setmodule.html.php b/module/admin/ui/setmodule.html.php index e48e869fe8..0f78f4d264 100644 --- a/module/admin/ui/setmodule.html.php +++ b/module/admin/ui/setmodule.html.php @@ -10,6 +10,10 @@ declare(strict_types=1); */ namespace zin; +global $app; +$app->loadLang('project'); +jsVar('confirmDisableStoryType', $lang->project->confirmDisableStoryType); + if(strpos(",$disabledFeatures,", ",productUR,") !== false) $disabledFeatures .= ',productER'; $rows = array(); diff --git a/module/ai/js/modeledit.ui.js b/module/ai/js/modeledit.ui.js index ce6f3840d2..4f750ba56c 100644 --- a/module/ai/js/modeledit.ui.js +++ b/module/ai/js/modeledit.ui.js @@ -27,9 +27,9 @@ window.testConnection = () => }); }; -const handleModelTypeChange = () => +const handleModelTypeChange = (modelType = '') => { - const modelType = $('select[name="type"]').val(); + if(!modelType) modelType = $('select[name="type"]').val(); const vendorList = window.vendorListLang[modelType]; $('select[name="vendor"]').html(''); for(const vendor in vendorList) $('select[name="vendor"]').append(''); @@ -37,9 +37,9 @@ const handleModelTypeChange = () => $('select[name="vendor"]').trigger('change'); }; -const handleVendorChange = () => +const handleVendorChange = (vendor = '') => { - const vendor = $('select[name="vendor"]').val(); + vendor ? $('select[name="vendor"]').val(vendor) : (vendor = $('select[name="vendor"]').val()); const requiredFields = window.vendorList[vendor]['credentials']; const vendorTip = window.vendorTipsLang[vendor]; $('.vendor-tips').html(vendorTip ? vendorTip : ''); @@ -53,8 +53,8 @@ const handleProxyTypeChange = () => $(() => { - handleModelTypeChange(); - handleVendorChange(); + handleModelTypeChange(window.modelData.type); + handleVendorChange(window.modelData.vendor); handleProxyTypeChange(); $('select[name="type"]').on('change', handleModelTypeChange); $('select[name="vendor"]').on('change', handleVendorChange); diff --git a/module/ai/ui/modeledit.html.php b/module/ai/ui/modeledit.html.php index 9e3deae316..fe60b47ec4 100644 --- a/module/ai/ui/modeledit.html.php +++ b/module/ai/ui/modeledit.html.php @@ -10,6 +10,7 @@ $allCredFields = array_unique(array_reduce((array)$config->ai->vendorList, jsVar('window.vendorList', $config->ai->vendorList); jsVar('window.vendorListLang', $lang->ai->models->vendorList); jsVar('window.vendorTipsLang', $lang->ai->models->vendorTips); +jsVar('window.modelData', $model); formPanel ( diff --git a/module/api/model.php b/module/api/model.php index 95eeea252f..35263b8851 100644 --- a/module/api/model.php +++ b/module/api/model.php @@ -1,7 +1,7 @@ loadModel('metric')->getResultByCode('count_of_daily_finished_task_in_user', array('user' => $this->app->user->account, 'year' => date('Y', $yesterday), 'month' => date('m', $yesterday), 'day' => date('d', $yesterday)), 'realtime', null, $this->config->vision); + $finishTaskGroup = $this->loadModel('metric')->getResultByCode('count_of_daily_finished_task_in_user', array('user' => $this->app->user->account, 'year' => date('Y', $yesterday), 'month' => date('m', $yesterday), 'day' => date('d', $yesterday)), 'cron', null, $this->config->vision); if(!empty($finishTaskGroup)) { $finishTaskGroup = reset($finishTaskGroup); @@ -448,7 +448,7 @@ class blockZen extends block /* 获取昨日解决的Bug数。 */ $fixBug = 0; - $fixBugGroup = $this->metric->getResultByCode('count_of_daily_fixed_bug_in_user', array('user' => $this->app->user->account, 'year' => date('Y', $yesterday), 'month' => date('m', $yesterday), 'day' => date('d', $yesterday))); + $fixBugGroup = $this->metric->getResultByCode('count_of_daily_fixed_bug_in_user', array('user' => $this->app->user->account, 'year' => date('Y', $yesterday), 'month' => date('m', $yesterday), 'day' => date('d', $yesterday)), 'cron', null, $this->config->vision); if(!empty($fixBugGroup)) { $fixBugGroup = reset($fixBugGroup); diff --git a/module/branch/model.php b/module/branch/model.php index 29a970bb5d..22fe39d6a7 100644 --- a/module/branch/model.php +++ b/module/branch/model.php @@ -1,7 +1,7 @@ dao->update(TABLE_BRANCH)->data($branch) ->where('id')->eq($branchID) ->batchCheck($this->config->branch->edit->requiredFields, 'notempty') - ->checkIF(!empty($branch->name)&& $branch->name != $oldBranch->name, 'name', 'unique', "product = $oldBranch->product") + ->checkIF(!empty($branch->name) && $branch->name != $oldBranch->name, 'name', 'unique', "product = $oldBranch->product && id != $oldBranch->id") ->exec(); if(dao::isError()) return false; diff --git a/module/bug/control.php b/module/bug/control.php index bda3be5ded..91a8c67111 100755 --- a/module/bug/control.php +++ b/module/bug/control.php @@ -269,7 +269,7 @@ class bug extends control $oldBug = $this->bug->getByID($bugID); if(!empty($_POST)) { - $formData = form::data($this->config->bug->form->edit); + $formData = form::data($this->config->bug->form->edit, $bugID); $bug = $this->bugZen->prepareEditExtras($formData, $oldBug); $this->bugZen->checkRquiredForEdit($bug); @@ -315,7 +315,7 @@ class bug extends control { /* 初始化 bug 数据。*/ /* Init bug data. */ - $bug = form::data($this->config->bug->form->assignTo)->add('id', $bugID)->get(); + $bug = form::data($this->config->bug->form->assignTo, $bugID)->add('id', $bugID)->get(); if($this->app->rawMethod == 'batchassignto') unset($bug->mailto); @@ -376,7 +376,7 @@ class bug extends control /* 构造 bug 的表单数据。*/ /* Structure the bug form data. */ - $bug = form::data($this->config->bug->form->confirm)->add('id', $bugID)->setDefault('confirmed', 1)->get(); + $bug = form::data($this->config->bug->form->confirm, $bugID)->add('id', $bugID)->setDefault('confirmed', 1)->get(); $this->bug->confirm($bug, $kanbanData); @@ -501,7 +501,7 @@ class bug extends control /* 获取bug信息。 */ /* Build bug data. */ - $bug = form::data($this->config->bug->form->activate)->setDefault('assignedTo', $oldBug->resolvedBy)->add('activatedCount', $oldBug->activatedCount + 1)->add('id', $bugID)->add('resolvedDate', null)->add('closedDate', null)->get(); + $bug = form::data($this->config->bug->form->activate, $bugID)->setDefault('assignedTo', $oldBug->resolvedBy)->add('activatedCount', $oldBug->activatedCount + 1)->add('id', $bugID)->add('resolvedDate', null)->add('closedDate', null)->get(); /* 解析看板信息。 */ /* Parse kanban information. */ @@ -550,7 +550,7 @@ class bug extends control { /* 设置bug信息。 */ /* Set bug information. */ - $bug = form::data($this->config->bug->form->close)->add('id', $bugID)->get(); + $bug = form::data($this->config->bug->form->close, $bugID)->add('id', $bugID)->get(); /* 设置额外变量。 */ /* Set extra variables. */ diff --git a/module/bug/js/common.ui.js b/module/bug/js/common.ui.js index 96493aac1d..0e5c94b262 100644 --- a/module/bug/js/common.ui.js +++ b/module/bug/js/common.ui.js @@ -7,6 +7,7 @@ function changeProduct(event) { const productID = $(event.target).val(); if(productID == undefined) return false; + if(productID == bug.product) return false; if(typeof(changeProductConfirmed) != 'undefined' && !changeProductConfirmed) { @@ -19,7 +20,7 @@ function changeProduct(event) } else { - $('#product').val(bug.product); // Revert old product id if confirm is no. + $('[name=product]').zui('picker').$.setValue(bug.product); // Revert old product id if confirm is no. } }}); } @@ -324,7 +325,7 @@ function loadProjectBuilds(projectID) if(typeof(branch) == 'undefined') branch = 0; const productID = $('[name="product"]').val(); - const oldOpenedBuild = $('[name^="openedBuild"]').val() ? $('[name^="openedBuild"]').val() : 0; + const oldOpenedBuild = $('[name^="openedBuild"]').val() ? $('[name^="openedBuild"]').val().toString() : 0; if(config.currentMethod == 'create') { @@ -343,14 +344,13 @@ function loadProjectBuilds(projectID) const openedLink = $.createLink('build', 'ajaxGetProjectBuilds', 'projectID=' + projectID + '&productID=' + productID + '&varName=openedBuild&build=' + oldOpenedBuild + '&branch=' + branch); $.getJSON(openedLink, function(data) { - let buildID = $('[name^="openedBuild"]').val(); let $buildPicker = $('[name^="openedBuild"]').zui('picker'); $buildPicker.render({items: data}); - $buildPicker.$.setValue(buildID); + $buildPicker.$.setValue(oldOpenedBuild); loadBuildActions(); }) - const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val() : 0; + const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val().toString() : 0; const resolvedLink = $.createLink('build', 'ajaxGetProductBuilds', 'productID=' + productID + '&varName=resolvedBuild&build=' + oldResolvedBuild + '&branch=' + branch); $.getJSON(resolvedLink, function(data) { @@ -388,7 +388,7 @@ function loadProductBuilds(productID, type = 'normal', buildBox = 'all') const openedLink = $.createLink('build', 'ajaxGetProductBuilds', 'productID=' + productID + '&varName=openedBuild&build=' + bug.openedBuild + '&branch=' + branch + '&type=' + type); $.getJSON(openedLink, function(data) { - let buildID = $('[name^="openedBuild"]').val(); + let buildID = $('[name^="openedBuild"]').val().toString(); let $buildPicker = $('[name^="openedBuild"]').zui('picker'); $buildPicker.render({items: data}); $buildPicker.$.setValue(buildID); @@ -397,7 +397,7 @@ function loadProductBuilds(productID, type = 'normal', buildBox = 'all') if(buildBox == 'all' || buildBox == 'resolvedBuild') { - const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val() : 0; + const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val().toString() : 0; const resolvedLink = $.createLink('build', 'ajaxGetProductBuilds', 'productID=' + productID + '&varName=resolvedBuild&build=' + oldResolvedBuild + '&branch=' + branch + '&type=' + type); $.getJSON(resolvedLink, function(data) { @@ -415,7 +415,7 @@ function loadExecutionBuilds(executionID, num) let branch = num != '' ? $('#branch' + num).val() : $('[name="branch"]').val(); let productID = num != '' ? $('#product' + num).val() : $('[name="product"]').val(); - const oldOpenedBuild = $('[name^="openedBuild"]' + num).val() ? $('[name^="openedBuild"]' + num).val() : 0; + const oldOpenedBuild = $('[name^="openedBuild"]' + num).val() ? $('[name^="openedBuild"]' + num).val().toString() : 0; if(typeof(branch) == 'undefined') branch = 'all'; if(typeof(productID) == 'undefined') productID = 0; @@ -441,7 +441,7 @@ function loadExecutionBuilds(executionID, num) $buildPicker.$.setValue(oldOpenedBuild); }); - const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val() : 0; + const oldResolvedBuild = $('[name="resolvedBuild"]').val() ? $('[name="resolvedBuild"]').val().toString() : 0; const resolvedLink = $.createLink('build', 'ajaxGetProductBuilds', 'productID=' + productID + '&varName=resolvedBuild&build=' + oldResolvedBuild + '&branch=' + branch); $.getJSON(resolvedLink, function(data) { @@ -687,3 +687,22 @@ let checkHasCheckedData = function(item, checkedValue) { return item.value == checkedValue; }; + +function loadBuilds() +{ + const productID = $('[name="product"]').val(); + const projectID = $('[name="project"]').val() == 'undefined' ? 0 : $('[name="project"]').val(); + const executionID = $('[name="execution"]').val() == 'undefined' ? 0 : $('[name="execution"]').val(); + if(executionID) + { + loadExecutionBuilds(executionID); + } + else if(projectID) + { + loadProjectBuilds(projectID); + } + else + { + loadProductBuilds(productID); + } +} diff --git a/module/bug/lang/de.php b/module/bug/lang/de.php index 2eed87eb90..5d465367c4 100644 --- a/module/bug/lang/de.php +++ b/module/bug/lang/de.php @@ -440,6 +440,7 @@ $lang->bug->notice->delayWarning = " Delay $lang->bug->notice->skipNotActive = "Bug %s has/have been resolved or closed, it won't be changed again"; $lang->bug->error = new stdclass(); -$lang->bug->error->notExist = "Bug doesn't exist."; -$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.'; -$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty."; +$lang->bug->error->notExist = "Bug doesn't exist."; +$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.'; +$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty."; +$lang->bug->error->duplicateBugNotExist = 'The duplicate bug does not exist.'; diff --git a/module/bug/lang/en.php b/module/bug/lang/en.php index 5e4b2c2aff..dccd2c6aa5 100644 --- a/module/bug/lang/en.php +++ b/module/bug/lang/en.php @@ -440,6 +440,7 @@ $lang->bug->notice->delayWarning = " Delay $lang->bug->notice->skipNotActive = "Bug %s has/have been resolved or closed, it won't be changed again"; $lang->bug->error = new stdclass(); -$lang->bug->error->notExist = "Bug doesn't exist."; -$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.'; -$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty."; +$lang->bug->error->notExist = "Bug doesn't exist."; +$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.'; +$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty."; +$lang->bug->error->duplicateBugNotExist = 'The duplicate bug does not exist.'; diff --git a/module/bug/lang/fr.php b/module/bug/lang/fr.php index a5ef81d684..f59b71ac7f 100644 --- a/module/bug/lang/fr.php +++ b/module/bug/lang/fr.php @@ -440,6 +440,7 @@ $lang->bug->notice->delayWarning = " Retar $lang->bug->notice->skipNotActive = "Bug %s has/have been resolved or closed, it won't be changed again"; $lang->bug->error = new stdclass(); -$lang->bug->error->notExist = "Bug doesn't exist."; -$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.'; -$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty."; +$lang->bug->error->notExist = "Bug doesn't exist."; +$lang->bug->error->cannotActivate = 'Bugs with a status other than Resolved or Closed cannot be activated.'; +$lang->bug->error->stepsNotEmpty = "The reproduction step cannot be empty."; +$lang->bug->error->duplicateBugNotExist = 'The duplicate bug does not exist.'; diff --git a/module/bug/lang/zh-cn.php b/module/bug/lang/zh-cn.php index 80f3066d9c..20c173e20b 100644 --- a/module/bug/lang/zh-cn.php +++ b/module/bug/lang/zh-cn.php @@ -440,6 +440,7 @@ $lang->bug->notice->delayWarning = " 延 $lang->bug->notice->skipNotActive = "Bug %s 已解决或已关闭,不再修改。"; $lang->bug->error = new stdclass(); -$lang->bug->error->notExist = 'Bug不存在。'; -$lang->bug->error->cannotActivate = '状态不是已解决或已关闭的Bug不能激活。'; -$lang->bug->error->stepsNotEmpty = "重现步骤不能为空。"; +$lang->bug->error->notExist = 'Bug不存在。'; +$lang->bug->error->cannotActivate = '状态不是已解决或已关闭的Bug不能激活。'; +$lang->bug->error->stepsNotEmpty = "重现步骤不能为空。"; +$lang->bug->error->duplicateBugNotExist = '重复Bug不存在。'; diff --git a/module/bug/model.php b/module/bug/model.php index 75fb30f306..68e14e0e37 100644 --- a/module/bug/model.php +++ b/module/bug/model.php @@ -410,6 +410,15 @@ class bugModel extends model { /* Get old bug. */ $oldBug = $this->getById($bug->id); + if(!empty($bug->duplicateBug)) + { + $duplicateBug = $this->fetchByID($bug->duplicateBug); + if(!$duplicateBug || $duplicateBug->deleted == '1' || $duplicateBug->product != $oldBug->product) + { + dao::$errors['duplicateBug'][] = $this->lang->bug->error->duplicateBugNotExist; + return false; + } + } /* Update bug. */ $this->dao->update(TABLE_BUG)->data($bug, 'buildName,createBuild,buildExecution,comment') @@ -1705,7 +1714,8 @@ class bugModel extends model ->fetchPairs('id'); if(empty($story)) $story = array(0); - $bugQuery = preg_replace("/`story`[ ]+(NOT[ ]*)?LIKE[ ]+'%$searchValue%'/Ui", '`story` $1 IN (' . implode(',', $story) .')', $bugQuery); + $searchValue = preg_quote($searchValue, '/'); + $bugQuery = preg_replace("/`story`[ ]+(NOT[ ]*)?LIKE[ ]+'%$searchValue%'/Ui", '`story` $1 IN (' . implode(',', $story) .')', $bugQuery); } } $bugQuery .= ' AND `story` != 0'; diff --git a/module/bug/test/lib/confirmbug.ui.class.php b/module/bug/test/lib/confirmbug.ui.class.php index 93f972d2af..4247d69914 100644 --- a/module/bug/test/lib/confirmbug.ui.class.php +++ b/module/bug/test/lib/confirmbug.ui.class.php @@ -113,7 +113,7 @@ class confirmBugTester extends tester { if(empty($bugTitle) || !is_object($list)) return $this->failed('获取bug标题失败'); - $bugTitleLists = $list->dom->bugTitleList->getElementList($list->dom->page->xpath['bugTitleList']); + $bugTitleLists = $list->dom->getElementList($list->dom->xpath['bugTitleList']); $bugList = array_map(function($element){return $element->getText();}, $bugTitleLists->element); if(!in_array($bugTitle, $bugList)) return $this->success('操作bug成功'); return $this->failed('操作bug失败'); diff --git a/module/bug/ui/create.html.php b/module/bug/ui/create.html.php index 88f3a999e2..2a5c04b8d5 100644 --- a/module/bug/ui/create.html.php +++ b/module/bug/ui/create.html.php @@ -16,11 +16,11 @@ include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); $fields = useFields('bug.create'); if(!empty($executionType) && $executionType == 'kanban') $fields->merge('bug.kanban'); -$fields->autoLoad('product', array('items' => 'product,module,openedBuild,execution,project,story,task,assignedTo', 'updateOrders' => true)) - ->autoLoad('branch', 'module,openedBuild,execution,project,story,task,assignedTo') +$fields->autoLoad('product', array('items' => 'product,module,execution,project,story,task,assignedTo', 'updateOrders' => true)) + ->autoLoad('branch', 'module,execution,project,story,task,assignedTo') ->autoLoad('module', 'assignedTo,story') - ->autoLoad('project', 'project,openedBuild,execution,story,task,assignedTo,injection,identify') - ->autoLoad('execution', 'execution,openedBuild,story,task,assignedTo') + ->autoLoad('project', 'project,execution,story,task,assignedTo,injection,identify') + ->autoLoad('execution', 'execution,story,task,assignedTo') ->autoLoad('allBuilds', 'openedBuild') ->autoLoad('allUsers', 'assignedTo') ->autoLoad('region', 'lane'); @@ -36,6 +36,7 @@ jsVar('projectExecutionPairs', $projectExecutionPairs); formGridPanel ( + on::change('[name="product"], [name="branch"], [name="project"], [name="execution"]', 'loadBuilds'), set::title($lang->bug->create), set::fields($fields), set::loadUrl($loadUrl) diff --git a/module/bug/ui/edit.html.php b/module/bug/ui/edit.html.php index fc963e20c5..6f9c4511c5 100644 --- a/module/bug/ui/edit.html.php +++ b/module/bug/ui/edit.html.php @@ -480,7 +480,7 @@ detailBody item ( set::name($lang->bug->resolvedDate), - datePicker + datetimePicker ( set::name('resolvedDate'), set::value($bug->resolvedDate) diff --git a/module/bug/ui/resolve.html.php b/module/bug/ui/resolve.html.php index 4d0a6f266a..a0f19371d2 100644 --- a/module/bug/ui/resolve.html.php +++ b/module/bug/ui/resolve.html.php @@ -129,7 +129,7 @@ formPanel ( set::width('1/3'), set::label($lang->bug->resolvedDate), - set::control('datePicker'), + set::control('datetimePicker'), set::name('resolvedDate'), set::value(helper::now()) ), diff --git a/module/bug/zen.php b/module/bug/zen.php index 56228c0220..57b5c8cf56 100644 --- a/module/bug/zen.php +++ b/module/bug/zen.php @@ -1015,16 +1015,18 @@ class bugZen extends bug $productID = (int)$bug->productID; $branch = (string)$bug->branch; $moduleID = (int)$bug->moduleID; + $projectID = (int)$bug->projectID; $executionID = (int)$bug->executionID; - if($executionID) + if($executionID || $projectID) { - $stories = $this->story->getExecutionStoryPairs($executionID, $productID, $branch, '', 'full', 'all', 'story', false); + $stories = $this->story->getExecutionStoryPairs($executionID ? $executionID : $projectID, $productID, $branch, '', 'full', 'all', 'story', false); } else { $stories = $this->story->getProductStoryPairs($productID, $branch, $moduleID, 'active,closed', 'id_desc', 0, '', 'story', false); } + if(!isset($stories[$bug->storyID])) { $bugStory = $this->story->fetchById($bug->storyID); @@ -1213,10 +1215,17 @@ class bugZen extends bug $assignedToList = array_filter($assignedToList); if(empty($assignedToList)) $assignedToList = $this->user->getPairs('devfirst|noclosed'); } - if(!isset($openedBuilds[$bug->openedBuild])) + $bugOpenedBuilds = explode(',', $bug->openedBuild); + if(!empty($bugOpenedBuilds)) { - $build = $this->build->getByID((int)$bug->openedBuild); - if($build) $openedBuilds[$bug->openedBuild] = $build->name; + foreach($bugOpenedBuilds as $bugOpenedBuild) + { + if(!isset($openedBuilds[$bugOpenedBuild])) + { + $build = $this->build->getByID((int)$bugOpenedBuild); + if($build) $openedBuilds[$bugOpenedBuild] = $build->name; + } + } } $openedBuilds = $this->build->addReleaseLabelForBuilds($bug->product, $openedBuilds); @@ -1272,7 +1281,7 @@ class bugZen extends bug */ protected function buildBugForResolve(object $oldBug): object { - $bug = form::data($this->config->bug->form->resolve) + $bug = form::data($this->config->bug->form->resolve, $oldBug->id) ->setDefault('assignedTo', $oldBug->openedBy) ->setDefault('resolvedDate', helper::now()) ->add('id', $oldBug->id) diff --git a/module/build/config.php b/module/build/config.php index 4729ae0ac2..1f2a03e46b 100644 --- a/module/build/config.php +++ b/module/build/config.php @@ -2,8 +2,8 @@ $config->build = new stdclass(); $config->build->create = new stdclass(); $config->build->edit = new stdclass(); -$config->build->create->requiredFields = 'product,name,builder,date'; -$config->build->edit->requiredFields = 'product,name,builder,date'; +$config->build->create->requiredFields = 'execution,product,name,builder,date'; +$config->build->edit->requiredFields = 'execution,product,name,builder,date'; $config->build->editor = new stdclass(); $config->build->editor->create = array('id' => 'desc', 'tools' => 'simpleTools'); diff --git a/module/build/control.php b/module/build/control.php index abda50d77d..e999de2e0a 100644 --- a/module/build/control.php +++ b/module/build/control.php @@ -47,6 +47,22 @@ class build extends control */ public function create(int $executionID = 0, int $productID = 0, int $projectID = 0) { + $this->loadModel('execution'); + $this->loadModel('project'); + + /* Set menu. */ + if($this->app->tab == 'project') + { + $this->project->setMenu($projectID); + } + elseif(in_array($this->app->tab, array('execution', 'qa'))) + { + $execution = $this->execution->getByID($executionID); + $projectID = $execution ? $execution->project : 0; + } + + if($this->app->tab == 'execution') $this->execution->setMenu($executionID); + if(!empty($_POST)) { $build = form::data()->setDefault('createdBy', $this->app->user->account)->get(); @@ -64,24 +80,9 @@ class build extends control return $this->sendSuccess(array('load' => $this->createLink($this->app->rawModule, 'view', "buildID=$buildID"), 'id' => $buildID)); } - $status = empty($this->config->CRProduct) ? 'noclosed' : ''; - $this->loadModel('execution'); - $this->loadModel('project'); - - /* Set menu. */ - if($this->app->tab == 'project') - { - $this->project->setMenu($projectID); - } - elseif(in_array($this->app->tab, array('execution', 'qa'))) - { - $execution = $this->execution->getByID($executionID); - $projectID = $execution ? $execution->project : 0; - } - - if($this->app->tab == 'execution') $this->execution->setMenu($executionID); if(in_array($this->app->tab, array('execution', 'project'))) $this->session->set('project', $projectID); + $status = empty($this->config->CRProduct) ? 'noclosed' : ''; $this->buildZen->assignCreateData($productID, $executionID, $projectID, $status); } @@ -97,7 +98,7 @@ class build extends control { if(!empty($_POST)) { - $build = form::data()->get(); + $build = form::data($this->config->build->form->edit, $buildID)->get(); $changes = $this->build->update($buildID, $build); if(dao::isError()) return $this->sendError(dao::getError()); diff --git a/module/build/model.php b/module/build/model.php index 9addd64c7c..ba81d4967a 100644 --- a/module/build/model.php +++ b/module/build/model.php @@ -479,6 +479,7 @@ class buildModel extends model } } if($relationBranch) $build->branch = implode(',', $relationBranch); + $this->config->build->create->requiredFields = str_replace('execution,', '', $this->config->build->create->requiredFields); } else { diff --git a/module/caselib/control.php b/module/caselib/control.php index 33a8f00b4f..a542004f95 100644 --- a/module/caselib/control.php +++ b/module/caselib/control.php @@ -73,7 +73,7 @@ class caselib extends control { if(!empty($_POST)) { - $formData = form::data($this->config->caselib->form->edit); + $formData = form::data($this->config->caselib->form->edit, $libID); $lib = $this->caselibZen->prepareEditExtras($formData, $libID); $this->caselib->update($lib); diff --git a/module/caselib/zen.php b/module/caselib/zen.php index 7f5e4b3043..0b8a661406 100644 --- a/module/caselib/zen.php +++ b/module/caselib/zen.php @@ -316,9 +316,9 @@ class caselibZen extends caselib $step = trim($step); if(empty($step)) continue; - preg_match('/^((([0-9]+)[.]([0-9]+))[.]([0-9]+))[.、](.*)$/U', $step, $out); - if(!$out) preg_match('/^(([0-9]+)[.]([0-9]+))[.、](.*)$/U', $step, $out); - if(!$out) preg_match('/^([0-9]+)[.、](.*)$/U', $step, $out); + preg_match('/^((([0-9]+)[.]([0-9]+))[.]([0-9]+))[.、](.*)$/Uu', $step, $out); + if(!$out) preg_match('/^(([0-9]+)[.]([0-9]+))[.、](.*)$/Uu', $step, $out); + if(!$out) preg_match('/^([0-9]+)[.、](.*)$/Uu', $step, $out); if($out) { $count = count($out); diff --git a/module/common/model.php b/module/common/model.php index c54d404478..5aabfabbfb 100644 --- a/module/common/model.php +++ b/module/common/model.php @@ -356,11 +356,12 @@ class commonModel extends model * * @param string $module * @param string $method + * @param int $objectID * @static * @access public * @return array */ - public static function formConfig(string $module, string $method): array + public static function formConfig(string $module, string $method, int $objectID = 0): array { global $config, $app; if($config->edition == 'open') return array(); @@ -368,7 +369,7 @@ class commonModel extends model $formConfig = array(); $common = new self(); $required = $common->dao->select('*')->from(TABLE_WORKFLOWRULE)->where('type')->eq('system')->andWhere('rule')->eq('notempty')->fetch(); - $fields = $common->loadModel('flow')->getExtendFields($module, $method); + $fields = $common->loadModel('flow')->getExtendFields($module, $method, $objectID); $type = 'string'; foreach($fields as $fieldObject) @@ -1296,7 +1297,7 @@ eof; * The following pages can be allowed to open in non-iframe, so ignore these pages. */ $module = $this->app->getModuleName(); - $whitelist = is_string($whitelist) ? $whitelist : '|index|tutorial|install|upgrade|sso|cron|misc|user-login|user-deny|user-logout|user-reset|user-forgetpassword|user-resetpassword|my-changepassword|my-preference|file-read|file-download|file-preview|file-uploadimages|file-ajaxwopifiles|report-annualdata|misc-captcha|execution-printkanban|traincourse-ajaxuploadlargefile|traincourse-playvideo|screen-view|zanode-create|screen-ajaxgetchart|ai-chat|integration-wopi|instance-terminal|'; + $whitelist = is_string($whitelist) ? $whitelist : '|index|tutorial|install|upgrade|sso|cron|misc|user-login|user-deny|user-logout|user-reset|user-forgetpassword|user-resetpassword|my-changepassword|my-preference|file-read|file-download|file-preview|file-uploadimages|file-ajaxwopifiles|report-annualdata|misc-captcha|execution-printkanban|traincourse-ajaxuploadlargefile|traincourse-playvideo|screen-view|zanode-create|screen-ajaxgetchart|ai-chat|integration-wopi|instance-terminal|conference-getconferencepermissions|'; $iframeList = '|cron-index|zanode-create|'; if(strpos($iframeList, "|{$module}-{$method}|") === false && (strpos($whitelist, "|{$module}|") !== false || strpos($whitelist, "|{$module}-{$method}|") !== false)) return true; @@ -2164,6 +2165,11 @@ eof; foreach($menu['subMenu'] as $key1 => $subMenu) { $lang->$moduleName->$menuKey->{$label}['subMenu']->$key1 = static::setMenuVarsEx($subMenu, $objectID, $params); + if(!isset($subMenu['dropMenu'])) continue; + foreach($subMenu['dropMenu'] as $key2 => $dropMenu) + { + $lang->$moduleName->$menuKey->{$label}['subMenu']->$key1['dropMenu']->$key2 = static::setMenuVarsEx($dropMenu, $objectID, $params); + } } } @@ -3532,7 +3538,7 @@ eof; if(!$config->message->browser->turnon) return; $showCount = $config->message->browser->count; - $unreadCount = $app->dbh->query("SELECT COUNT(1) as count FROM " . TABLE_NOTIFY . " WHERE `objectType` = 'message' AND status != 'read' AND `toList` = ',{$app->user->account},'")->fetch()->count; + $unreadCount = $app->dbh->query("SELECT COUNT(1) AS `count` FROM " . TABLE_NOTIFY . " WHERE `objectType` = 'message' AND status != 'read' AND `toList` = ',{$app->user->account},'")->fetch()->count; $dotStyle = static::getDotStyle($showCount != '0', $unreadCount); if($unreadCount > 99) $unreadCount = '99+'; diff --git a/module/common/tao.php b/module/common/tao.php index a262ba3fa1..fde58809ba 100644 --- a/module/common/tao.php +++ b/module/common/tao.php @@ -171,6 +171,8 @@ class commonTao extends commonModel if(is_numeric($preAndNextObject->next)) $preAndNextObject->next = $objectIdIndex + 1 < count($objectIdList) ? $objects[$objectIdList[$objectIdIndex + 1]] : ''; } + $preAndNextObject->idKey = $key; + return $preAndNextObject; } @@ -213,13 +215,14 @@ class commonTao extends commonModel */ protected static function checkPrivByRights(string $module, string $method, array $acls, mixed $object): bool { - global $lang; + global $lang, $app; if(!commonModel::hasDBPriv($object, $module, $method)) return false; if(empty($acls['views'])) return true; $menu = isset($lang->navGroup->$module) ? $lang->navGroup->$module : $module; if($module == 'my' and $method == 'team') $menu = 'system'; // Fix bug #18642. $menu = strtolower($menu); + if($menu == 'product' && $app->tab != 'product' && in_array($module, array('story', 'requirement', 'epic'))) return true; if($menu != 'qa' and $menu != 'project' and !isset($lang->$menu->menu)) return true; if(($menu == 'my' and $method != 'team') or $menu == 'index' or $module == 'tree') return true; if($module == 'company' and $method == 'dynamic') return true; diff --git a/module/compile/model.php b/module/compile/model.php index e4803b4af9..fc724081f5 100644 --- a/module/compile/model.php +++ b/module/compile/model.php @@ -1,7 +1,7 @@ buildin == 0 and common::hasPriv('cron', 'delete')) $actionsHtml .= html::a(inlink('delete', "id={$cron->id}"), $lang->delete, '', "class='primary-500 ajax-submit' data-confirm={$lang->cron->confirmDelete}"); $cron->actions = $actionsHtml; + if($cron->command == '') $cron->lastTime = ''; if($cron->lastTime) $cron->lastTime = substr($cron->lastTime, 2, 17); } diff --git a/module/custom/model.php b/module/custom/model.php index 856d818889..186aa02802 100644 --- a/module/custom/model.php +++ b/module/custom/model.php @@ -1,6 +1,6 @@ custom->{$module}->fields)) if($module == 'approvalflow') { $currentModule = 'approvalflow'; - if(in_array($key, array('project', 'workflow'))) - { - $method = 'browse'; - $params = "type=$key"; - $active = (isset($type) and $type == $key) ? 'active' : $active; - } + $method = $key; + $params = ''; } if($module == 'setDate') diff --git a/module/datatable/model.php b/module/datatable/model.php index 172cf841b2..006f0f0024 100644 --- a/module/datatable/model.php +++ b/module/datatable/model.php @@ -149,6 +149,17 @@ class datatableModel extends model } if(in_array($module, array('product', 'project', 'execution')) and empty($this->config->setCode)) unset($fieldSetting['code']); + + if($module == 'story' && $method == 'browse' && $this->app->tab == 'project') + { + $project = $this->loadModel('project')->getByID((int)$this->session->project); + $projectModel = $project->model; + if(($projectModel == 'waterfall' || $projectModel == 'waterfallplus') && isset($fieldSetting['plan'])) + { + unset($fieldSetting['plan']); + } + } + } uasort($fieldSetting, array('datatableModel', 'sortCols')); diff --git a/module/dept/model.php b/module/dept/model.php index 781405c94f..f11cf7259d 100644 --- a/module/dept/model.php +++ b/module/dept/model.php @@ -353,7 +353,8 @@ class deptModel extends model $type = $type == 'outside' ? 'outside' : 'inside'; return $this->dao->select("$keyField, realname")->from(TABLE_USER) - ->where('deleted')->eq(0) + ->where('1=1') + ->beginIF(strpos($params, 'queryAll') === false)->andWhere('deleted')->eq(0)->fi() ->beginIF(strpos($params, 'all') === false)->andWhere('type')->eq($type)->fi() ->beginIF($childDepts)->andWhere('dept')->in($childDepts)->fi() ->beginIF($this->config->vision)->andWhere("CONCAT(',', visions, ',')")->like("%,{$this->config->vision},%")->fi() diff --git a/module/design/control.php b/module/design/control.php index d59a5a103c..868c97f6b4 100755 --- a/module/design/control.php +++ b/module/design/control.php @@ -37,10 +37,11 @@ class design extends control * @param int $projectID * @param int $productID * @param int $designID + * @param string $type * @access public * @return void */ - public function commonAction(int $projectID = 0, int $productID = 0, int $designID = 0) + public function commonAction(int $projectID = 0, int $productID = 0, int $designID = 0, string $type = 'all') { $products = $this->product->getProductPairsByProject($projectID); $products[0] = $this->lang->product->all; @@ -49,6 +50,7 @@ class design extends control $productID = $this->product->getAccessibleProductID($productID, $products); + $this->designZen->setMenu($projectID, $productID, strtolower($type)); $this->project->setMenu($projectID); $project = $this->project->getByID($projectID); @@ -79,7 +81,7 @@ class design extends control */ public function browse(int $projectID = 0, int $productID = 0, string $type = 'all', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 20, int $pageID = 1) { - $productID = $this->commonAction($projectID, $productID); + $productID = $this->commonAction($projectID, $productID, 0, $type); $project = $this->project->getByID($projectID); /* Save session for design list. */ @@ -132,7 +134,7 @@ class design extends control */ public function create(int $projectID = 0, int $productID = 0, string $type = 'all') { - $productID = $this->commonAction($projectID, $productID); + $productID = $this->commonAction($projectID, $productID, 0, $type); if($_POST) { @@ -175,7 +177,7 @@ class design extends control */ public function batchCreate(int $projectID = 0, int $productID = 0, string $type = 'all') { - $productID = $this->commonAction($projectID, $productID); + $productID = $this->commonAction($projectID, $productID, 0, $type); if($_POST) { @@ -213,7 +215,7 @@ class design extends control public function view(int $designID = 0) { $design = $this->design->getByID($designID); - $this->commonAction($design->project, $design->product, $designID); + $this->commonAction($design->project, $design->product, $designID, $design->type); $this->session->set('revisionList', $this->app->getURI(true)); $this->session->set('storyList', $this->app->getURI(true), 'product'); @@ -246,7 +248,7 @@ class design extends control { $design = $this->design->getByID($designID); $design = $this->design->getAffectedScope($design); - $this->commonAction($design->project, $design->product, $designID); + $this->commonAction($design->project, $design->product, $designID, $design->type); if($_POST) { @@ -301,7 +303,7 @@ class design extends control } $design = $this->design->getByID($designID); - $this->commonAction($design->project, $design->product, $designID); + $this->commonAction($design->project, $design->product, $designID, $design->type); /* Get project and date. */ $project = $this->loadModel('project')->getByID($design->project); @@ -384,7 +386,7 @@ class design extends control $pager = pager::init(0, $recPerPage, $pageID); $design = $this->design->getCommit($designID, $pager); - $this->commonAction($design->project, $design->product, $designID); + $this->commonAction($design->project, $design->product, $designID, $design->type); $this->config->design->viewcommit->dtable->fieldList['actions']['list']['unlinkCommit']['url'] = sprintf($this->config->design->viewcommit->actionList['unlinkCommit']['url'], $designID); diff --git a/module/design/js/browse.ui.js b/module/design/js/browse.ui.js deleted file mode 100644 index cdf5a62d0c..0000000000 --- a/module/design/js/browse.ui.js +++ /dev/null @@ -1,27 +0,0 @@ -$(function() -{ - type = type == 'bysearch' ? 'all' : type; - $('#mainNavbar .nav a').removeClass('active'); - $('#mainNavbar .nav a[data-id=' + type + ']').addClass('active'); - - $('#mainNavbar .nav li > a').each(function() - { - let type = $(this).attr('data-id'); - if(type == 'more') - { - $(this).attr('href', '###'); - return; - } - - let href = $.createLink('design', 'browse', 'projectID=' + projectID + '&productID=' + productID + '&type=' + type); - $(this).attr('href', href); - }); - - if($('#mainNavbar .dropdown-menu a[data-id=' + type +']').hasClass('active')) - { - let typeName = $('#mainNavbar a[data-id=' + type +']').html(); - - $('#mainNavbar').find(".dropdown-hover > a").html(typeName + ""); - $("#mainNavbar li[data-id='more']").addClass('active'); - } -}) diff --git a/module/design/js/common.ui.js b/module/design/js/common.ui.js index 3a7a0a777a..2d95688f04 100644 --- a/module/design/js/common.ui.js +++ b/module/design/js/common.ui.js @@ -17,12 +17,3 @@ function loadStory(e) $storyPicker.$.setValue(storyID); }) } - -$(function() -{ - if(typeof type != 'undefined') - { - $('#mainNavbar .nav a').removeClass('active'); - $('#mainNavbar .nav a[data-id=' + type + ']').addClass('active'); - } -}) diff --git a/module/design/zen.php b/module/design/zen.php new file mode 100644 index 0000000000..ef6ad407ab --- /dev/null +++ b/module/design/zen.php @@ -0,0 +1,54 @@ + + * @package desgin + * @link https://www.zentao.net + */ +class designZen extends control +{ + /** + * 设置设计导航。 + * Set design menu. + * + * @param int $projectID + * @param int $productID + * @param string $type + * @access public + * @return void + */ + public function setMenu(int $projectID, int $productID = 0, string $type = ''): void + { + $project = $this->loadModel('project')->getByID($projectID); + if(empty($project)) return; + + if(!empty($project) and (in_array($project->model, array('waterfall', 'ipd')))) $typeList = 'typeList'; + if(!empty($project) and $project->model == 'waterfallplus') $typeList = 'plusTypeList'; + if(!isset($typeList)) return; + + /* Show custom design types. */ + $this->lang->waterfall->menu->design['subMenu'] = new stdclass(); + $this->lang->waterfall->menu->design['subMenu']->all = array('link' => "{$this->lang->all}|design|browse|projectID=%s&productID={$productID}&browseType=all", 'exclude' => $type == 'all' ? '' : 'design'); + $count = 1; + foreach(array_filter($this->lang->design->{$typeList}) as $key => $value) + { + $key = strtolower($key); + $exclude = $type == $key ? '' : 'design'; + $alias = $type == $key ? $this->app->rawMethod : ''; + + if($count <= 4) $this->lang->waterfall->menu->design['subMenu']->$key = array('link' => "{$value}|design|browse|projectID=%s&productID={$productID}&browseType={$key}", 'exclude' => $exclude, 'alias' => $alias); + if($count == 5) + { + $this->lang->waterfall->menu->design['subMenu']->more = array('link' => "{$this->lang->design->more}|design|browse|projectID=%s&productID={$productID}&browseType={$key}", 'class' => 'dropdown dropdown-hover', 'exclude' => $exclude, 'alias' => $alias); + $this->lang->waterfall->menu->design['subMenu']->more['dropMenu'] = new stdclass(); + } + if($count >= 5) $this->lang->waterfall->menu->design['subMenu']->more['dropMenu']->$key = array('link' => "{$value}|design|browse|projectID=%s&productID={$productID}&browseType={$key}", 'exclude' => $exclude, 'alias' => $alias); + + $count ++; + } + } +} diff --git a/module/doc/control.php b/module/doc/control.php index 48508de856..fa203b77b0 100755 --- a/module/doc/control.php +++ b/module/doc/control.php @@ -195,7 +195,7 @@ class doc extends control { $lib = $this->doc->getLibByID($libID); $this->view->spaces = $this->doc->getTeamSpaces(); - $this->view->spaceID = !empty($lib->parent) ? $lib->parent : $libID; + $this->view->spaceID = !empty($lib->parent) ? $lib->parent : $objectID; } $this->docZen->setAclForCreateLib($type); @@ -769,7 +769,7 @@ class doc extends control $objectType = isset($lib->type) ? $lib->type : 'custom'; $objectID = zget($doc, $objectType, 0); - if($objectType == 'custom') $this->lang->doc->menu->custom['alias'] = 'teamspace,view'; + if($objectType == 'custom') $objectID = $lib->parent; list($libs, $libID, $object, $objectID, $objectDropdown) = $this->doc->setMenuByType($objectType, $objectID, (int)$doc->lib, (int)$appendLib); /* Get doc. */ diff --git a/module/doc/ui/view.html.php b/module/doc/ui/view.html.php index 53b0ba6bb0..933b6d5920 100644 --- a/module/doc/ui/view.html.php +++ b/module/doc/ui/view.html.php @@ -172,6 +172,7 @@ $docHeader = div set::type('ghost'), set::url(createLink('doc', 'edit', "docID=$doc->id")), $doc->type != 'text' ? setData('toggle', 'modal') : null, + setData('app', $app->tab), set::icon('edit') ) : null, dropdown diff --git a/module/doc/zen.php b/module/doc/zen.php index 61b6a27020..acf5e15db2 100644 --- a/module/doc/zen.php +++ b/module/doc/zen.php @@ -273,7 +273,6 @@ class docZen extends doc if($type == 'project' && $this->post->project) $objectID = $this->post->project; if($type == 'product' && $this->post->product) $objectID = $this->post->product; if($type == 'execution' && $this->post->execution) $objectID = $this->post->execution; - if($type == 'custom') $objectID = 0; $type = $type == 'execution' && $this->app->tab != 'execution' ? 'project' : $type; @@ -400,7 +399,7 @@ class docZen extends doc if(empty($locateLink)) { if($space == 'mine') $locateLink = $this->createLink('doc', 'mySpace', "type=mine&libID={$libID}"); - elseif(is_numeric($space)) $locateLink = $this->createLink('doc', 'teamSpace', "objectID=0&libID={$libID}"); + elseif(is_numeric($space)) $locateLink = $this->createLink('doc', 'teamSpace', "objectID={$space}&libID={$libID}"); else $locateLink = true; } if($locateLink === 'true') $locateLink = true; diff --git a/module/editor/model.php b/module/editor/model.php index 769e41032a..543c3a3d6d 100644 --- a/module/editor/model.php +++ b/module/editor/model.php @@ -1,6 +1,6 @@ execution->search['fields']['desc'] = $lang->task->desc; $config->execution->search['fields']['assignedTo'] = $lang->task->assignedTo; $config->execution->search['fields']['pri'] = $lang->task->pri; -if($app->tab == 'my') $config->execution->search['fields']['project'] = $lang->task->project; +$config->execution->search['fields']['project'] = $lang->task->project; $config->execution->search['fields']['execution'] = $lang->task->execution; $config->execution->search['fields']['module'] = $lang->task->module; $config->execution->search['fields']['estimate'] = $lang->task->estimate; @@ -92,7 +92,7 @@ $config->execution->search['params']['desc'] = array('operator' => 'in $config->execution->search['params']['assignedTo'] = array('operator' => '=', 'control' => 'select', 'values' => 'users'); $config->execution->search['params']['pri'] = array('operator' => '=', 'control' => 'select', 'values' => $lang->task->priList); -if($app->tab == 'my') $config->execution->search['params']['project'] = array('operator' => '=', 'control' => 'select', 'values' => ''); +$config->execution->search['params']['project'] = array('operator' => '=', 'control' => 'select', 'values' => ''); $config->execution->search['params']['execution'] = array('operator' => '=', 'control' => 'select', 'values' => ''); $config->execution->search['params']['module'] = array('operator' => 'belong', 'control' => 'select', 'values' => ''); $config->execution->search['params']['estimate'] = array('operator' => '=', 'control' => 'input', 'values' => ''); diff --git a/module/execution/control.php b/module/execution/control.php index 283001b04c..d357305619 100644 --- a/module/execution/control.php +++ b/module/execution/control.php @@ -479,7 +479,8 @@ class execution extends control $this->executionZen->assignCountForStory($executionID, $stories, $storyType); $this->executionZen->assignRelationForStory($execution, $products, $productID, $type, $storyType, $param, $orderBy, $pager); - $this->view->project = $project; + $this->view->project = $project; + $this->view->linkedProductCount = count($products); $this->display(); } @@ -1107,7 +1108,7 @@ class execution extends control $oldExecution = $this->dao->findById($executionID)->from(TABLE_EXECUTION)->fetch(); /* Get the data from the post. */ - $formData = form::data() + $formData = form::data(null, $executionID) ->add('id', $executionID) ->setDefault('lastEditedBy', $this->app->user->account) ->setDefault('lastEditedDate', helper::now()) @@ -1247,6 +1248,7 @@ class execution extends control $this->config->excludeDropmenuList[] = 'execution-batchedit'; } + $this->extendRequireFields($executionID); if($this->post->name) { $postData = fixer::input('post')->get(); @@ -1371,6 +1373,7 @@ class execution extends control $executionID = $execution->id; if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban; + $this->extendRequireFields($executionID); if(!empty($_POST)) { $postData = fixer::input('post') @@ -1416,6 +1419,7 @@ class execution extends control $execution = $this->commonAction($executionID); $executionID = $execution->id; + $this->extendRequireFields($executionID); if(!empty($_POST)) { $postData = fixer::input('post') @@ -1457,6 +1461,7 @@ class execution extends control $executionID = $execution->id; if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban; + $this->extendRequireFields($executionID); if(!empty($_POST)) { $postData = fixer::input('post') @@ -1504,6 +1509,7 @@ class execution extends control $executionID = $execution->id; if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban; + $this->extendRequireFields($executionID); if(!empty($_POST)) { $postData = fixer::input('post') @@ -1559,6 +1565,7 @@ class execution extends control $executionID = $execution->id; if($execution->type == 'kanban') $this->lang->executionCommon = $this->lang->execution->kanban; + $this->extendRequireFields($executionID); if(!empty($_POST)) { $now = helper::now(); diff --git a/module/execution/js/importbug.ui.js b/module/execution/js/importbug.ui.js index d9cc9a6819..a7a8bed916 100644 --- a/module/execution/js/importbug.ui.js +++ b/module/execution/js/importbug.ui.js @@ -29,3 +29,8 @@ function printError(result) zui.Modal.alert(msg); }) } + +window.onSearchLinks = function(type, result) +{ + loadComponent('#table-execution-importbug', {url: result.load, component: 'dtable', partial: true}); +}; diff --git a/module/execution/js/task.ui.js b/module/execution/js/task.ui.js index e608aa5090..ca06b4daf9 100644 --- a/module/execution/js/task.ui.js +++ b/module/execution/js/task.ui.js @@ -145,7 +145,7 @@ window.renderCell = function(result, info) } if(info.col.name == 'assignedTo' && result) { - if(!task.assignedTo && task.mode == 'multi' && !['done,closed'].includes(task.status)) + if(task.mode == 'multi' && !['done,closed'].includes(task.status)) { result[0]['props']['children'][1]['props']['children'] = teamLang; } diff --git a/module/execution/model.php b/module/execution/model.php index b947dea59f..b58df1612a 100755 --- a/module/execution/model.php +++ b/module/execution/model.php @@ -2035,10 +2035,20 @@ class executionModel extends model if(strpos($this->session->taskQuery, "deleted =") === false) $this->session->set('taskQuery', $this->session->taskQuery . " AND deleted = '0'"); $taskQuery = $this->session->taskQuery; + /* Limit current execution when no execution. */ - if(strpos($taskQuery, "`execution` =") === false) $taskQuery .= " AND `execution` = $executionID"; + if(strpos($taskQuery, "`execution` =") === false && strpos($taskQuery, "`project` =") === false) $taskQuery .= " AND `execution` = $executionID"; $executionQuery = "`execution` " . helper::dbIN(array_keys($executions)); $taskQuery = str_replace("`execution` = 'all'", $executionQuery, $taskQuery); // Search all execution. + if(strpos($taskQuery, "`execution`") === false) $taskQuery .= " AND `execution` " . helper::dbIN(array_keys($executions)); + /* Process all project query. */ + if(strpos($taskQuery, "`project` = 'all'") !== false) + { + $projects = $this->loadModel('project')->getPairsByProgram(); + $projectQuery = "`project` " . helper::dbIN(array_keys($projects)); + $taskQuery = str_replace("`project` = 'all'", $projectQuery, $taskQuery);; + } + $this->session->set('taskQueryCondition', $taskQuery, $this->app->tab); $this->session->set('taskOnlyCondition', true, $this->app->tab); @@ -2273,7 +2283,7 @@ class executionModel extends model foreach($products as $productID => $product) { $productPairs[$product->id] = $product->name; - $planGroup = $this->productplan->getBranchPlanPairs($productID, array(BRANCH_MAIN) + $product->branches, 'unexpired', true); + $planGroup = $this->productplan->getBranchPlanPairs($productID, array(BRANCH_MAIN) + $product->branches, '', true); foreach($planGroup as $plans) $planPairs += $plans; if($product->type == 'normal') continue; @@ -3715,15 +3725,21 @@ class executionModel extends model { if(strpos($condition, '`assignedTo`') !== false) { - preg_match("/`assignedTo`\s+(([^']*) ('([^']*)'))/", $condition, $matches); + preg_match_all("/`assignedTo`\s+(([^']*) ('([^']*)'))/", $condition, $matches); $condition = preg_replace('/`(\w+)`/', 't1.`$1`', $condition); - $condition = str_replace("t1.$matches[0]", "(t1.$matches[0] or (t1.mode = 'multi' and t2.`account` $matches[1] and t1.status != 'closed' and t2.status != 'done') )", $condition); + foreach($matches[0] as $matchIndex => $match) $condition = str_replace("t1.{$match}", "(t1.{$match} or (t1.mode = 'multi' and t2.`account` {$matches[1][$matchIndex]} and t1.status != 'closed' and t2.status != 'done') )", $condition); $this->session->set('taskQueryCondition', $condition, $this->app->tab); } $sql = $this->dao->select('t1.id')->from(TABLE_TASK)->alias('t1'); - if(strpos($condition, '`assignedTo`') !== false) $sql = $sql->leftJoin(TABLE_TASKTEAM)->alias('t2')->on("t2.task = t1.id and t2.account $matches[1]"); + if(strpos($condition, '`assignedTo`') !== false) + { + $onSQL = ''; + foreach($matches[1] as $matchIndex => $match) $onSQL .= "t2.account {$match} or "; + $onSQL = trim($onSQL, ' or '); + $sql = $sql->leftJoin(TABLE_TASKTEAM)->alias('t2')->on("t2.task = t1.id and ({$onSQL})"); + } $orderBy = array_map(function($value){return 't1.' . $value;}, explode(',', $orderBy)); $orderBy = implode(',', $orderBy); @@ -4102,6 +4118,9 @@ class executionModel extends model $this->config->execution->search['queryID'] = $queryID; $this->config->execution->search['params']['execution']['values'] = array(''=>'', $executionID => $executions[$executionID], 'all' => $this->lang->execution->allExecutions); + $projects = $this->loadModel('project')->getPairsByProgram(); + $this->config->execution->search['params']['project']['values'] = $projects + array('all' => $this->lang->project->allProjects); + $showAllModule = isset($this->config->execution->task->allModule) ? $this->config->execution->task->allModule : ''; $this->config->execution->search['params']['module']['values'] = $this->loadModel('tree')->getTaskOptionMenu($executionID, 0, $showAllModule ? 'allModule' : ''); @@ -4591,7 +4610,10 @@ class executionModel extends model */ public function buildTree(array $trees, bool $hasProduct = true, array $gradeGroup = array()): array { - $treeData = array(); + $treeData = array(); + $canViewTask = common::hasPriv('execution', 'treeTask'); + $canViewStory = common::hasPriv('execution', 'treeStory'); + foreach($trees as $index => $tree) { $tree = (object)$tree; @@ -4601,9 +4623,9 @@ class executionModel extends model { case 'task': $label = $tree->parent > 0 ? $this->lang->task->children : $this->lang->task->common; - $treeData[$index]['url'] = helper::createLink('execution', 'treeTask', "taskID={$tree->id}"); + $treeData[$index]['url'] = $canViewTask ? helper::createLink('execution', 'treeTask', "taskID={$tree->id}") : ''; $treeData[$index]['content'] = array( - 'html' => "', + 'html' => "', ); break; case 'product': @@ -4615,9 +4637,9 @@ class executionModel extends model $this->app->loadLang('story'); $gradePairs = zget($gradeGroup, $tree->type, array()); $grade = zget($gradePairs, $tree->grade, $tree->grade); - $treeData[$index]['url'] = helper::createLink('execution', 'treeStory', "taskID={$tree->storyId}"); + $treeData[$index]['url'] = $canViewStory ? helper::createLink('execution', 'treeStory', "taskID={$tree->storyId}") : ''; $treeData[$index]['content'] = array( - 'html' => "', + 'html' => "', ); break; case 'requirement': @@ -4965,7 +4987,7 @@ class executionModel extends model $postData->uid = ''; /* Handle extend fields. */ - $extendFields = $this->loadModel('project')->getFlowExtendFields(); + $extendFields = $this->loadModel('project')->getFlowExtendFields($projectID); foreach($extendFields as $field) $_POST[$field->field] = $project->field; if(isset($this->config->setCode) and $this->config->setCode == 1) $postData->code = $project->code; diff --git a/module/execution/test/lib/activateexecution.ui.class.php b/module/execution/test/lib/activateexecution.ui.class.php new file mode 100644 index 0000000000..743ee62147 --- /dev/null +++ b/module/execution/test/lib/activateexecution.ui.class.php @@ -0,0 +1,82 @@ +initForm('execution', 'view', array('execution' => $executionId ), 'appIframe-execution'); + $form->dom->btn($this->lang->execution->activate)->click(); + if(isset($end)) $form->dom->end->datePicker($end); + $form->dom->activateSubmit->click(); + $form->wait(1); + } + + /** + * 激活执行。 + * Activate execution. + * + * @param string $end + * @param string $executionId + * @access public + * @return bool + */ + public function activate($end, $executionId) + { + $this->inputFields($end, $executionId); + $form = $this->loadPage(); + + if($form->dom->activateSubmit === true) return $this->failed('激活执行失败'); + if($form->dom->plannedEnd->getText() != $end) return $this->failed('计划完成时间错误'); + return $this->success('激活执行成功'); + } + + /** + * 激活执行时计划完成日期小于计划开始日期。 + * Activate execution with end less than begin. + * + * @param string $end + * @param string $executionId + * @access public + * @return bool + */ + public function activateWithLessEnd($end, $executionId) + { + $this->inputFields($end, $executionId); + $form = $this->loadPage(); + $info = sprintf($this->lang->execution->errorLesserPlan, $end, date('Y-m-d')); + if($form->dom->endTip->getText() == $info) return $this->success('激活执行表单页提示信息正确'); + return $this->failed('激活执行表单页提示信息不正确'); + } + + /** + * 激活执行时计划完成日期大于项目计划完成日期。 + * Activate execution with end greater than begin. + * + * @param string $end + * @param string $executionId + * @access public + * @return bool + */ + public function activateWithGreaterEnd($end, $executionId) + { + $this->inputFields($end, $executionId); + $form = $this->loadPage(); + $info = sprintf($this->lang->execution->errorGreaterParent, ''); + $text = $form->dom->endTip->getText(); + /* 获取页面返回信息中除日期外的内容 */ + preg_match_all('/(\d{4}-\d{2}-\d{2})/', $text, $matches); ~ + $date = $matches[0][0]; ~ + $params = str_replace($date, '', $text); ~ + $params = trim($params); + if($params == $info) return $this->success('激活执行表单页提示信息正确'); + return $this->failed('激活执行表单页提示信息不正确'); + } +} diff --git a/module/execution/test/lib/closeexecution.ui.class.php b/module/execution/test/lib/closeexecution.ui.class.php new file mode 100644 index 0000000000..d67861fd6b --- /dev/null +++ b/module/execution/test/lib/closeexecution.ui.class.php @@ -0,0 +1,78 @@ +initForm('execution', 'view', array('execution' => $executionId), 'appIframe-execution'); + $form->dom->btn($this->lang->execution->close)->click(); + if(isset($realEnd)) $form->dom->realEnd->datePicker($realEnd); + $form->wait(1); + $form->dom->closeSubmit->click(); + $form->wait(1); + } + + /** + * 正常关闭执行。 + * Close execution. + * + * @param string $realEnd + * @access public + * @return bool + */ + public function close($realEnd) + { + $this->inputFields($realEnd); + $form = $this->loadPage(); + if($form->dom->status->getText() != $this->lang->execution->statusList->closed) return $this->failed('执行状态错误'); + if($form->dom->realEndView->getText() != $realEnd) return $this->failed('执行实际完成日期错误'); + return $this->success('关闭执行成功'); + } + + /** + * 实际完成日期大于当前日期。 + * The real end date is greater than the current date. + * + * @param string $realEnd + * @access public + * @return bool + */ + public function closeWithGreaterDate($realEnd) + { + $this->inputFields($realEnd); + $form = $this->loadPage(); + $field = $form->dom->realEndField->getText(); + $text = $form->dom->realEndTip->getText(); + $info = sprintf($this->lang->error->le, $field, date('Y-m-d')); + if($text == $info) return $this->success('关闭执行表单页提示信息正确'); + return $this->failed('关闭执行表单页提示信息不正确'); + } + + /** + * 实际完成日期小于实际开始日期。 + * The real end date is less than the real start date. + * + * @param string $realEnd + * @access public + * @return bool + */ + public function closeWithLessDate($realEnd) + { + $this->inputFields($realEnd, $executionId = '110'); + $form = $this->loadPage(); + $realBegan = $form->dom->realBeganView->getText(); + $field = $form->dom->realEndField->getText(); + $text = $form->dom->realEndTip->getText(); + $info = sprintf($this->lang->execution->ge, $field, $realBegan); + if($text == $info) return $this->success('关闭执行表单页提示信息正确'); + return $this->failed('关闭执行表单页提示信息不正确'); + } +} diff --git a/module/execution/test/lib/putoffexecution.ui.class.php b/module/execution/test/lib/putoffexecution.ui.class.php new file mode 100644 index 0000000000..4d853ec2db --- /dev/null +++ b/module/execution/test/lib/putoffexecution.ui.class.php @@ -0,0 +1,102 @@ +initForm('execution', 'view', array('execution' => $executionId ), 'appIframe-execution'); + $status = $form->dom->status->getText(); + $form->dom->btn($this->lang->execution->putoff)->click(); + if(isset($execution['begin'])) $form->dom->begin->datePicker($execution['begin']); + if(isset($execution['end'])) $form->dom->end->datePicker($execution['end']); + if(isset($execution['days'])) $form->dom->days->setValue($execution['days']); + $form->dom->putoffSubmit->click(); + $form->wait(1); + return $status; + } + + /** + * 延期执行。 + * Putoff execution. + * + * @param array $execution + * @param string $executionId + * @access public + * @return bool + */ + public function putoff($execution, $executionId) + { + $status = $this->inputFields($execution, $executionId); + $form = $this->loadPage(); + + if($form->dom->putoffSubmit === true) return $this->failed('延期失败'); + if($form->dom->status->getText() != $status) return $this->failed('执行状态错误'); + if(isset($execution['begin']) && $form->dom->plannedBegin->getText() != $execution['begin']) return $this->failed('计划开始时间错误'); + if(isset($execution['end']) && $form->dom->plannedEnd->getText() != $execution['end']) return $this->failed('计划完成时间错误'); + return $this->success('延期执行成功'); + } + + /** + * 延期执行的计划起止日期不正确。 + * Putoff execution with wrong date. + * + * @param array $execution + * @param string $executionId + * @param string $dateType begin|end + * @access public + * @return bool + */ + public function putoffWithWrongDate($execution, $executionId, $dateType = 'end') + { + $this->inputFields($execution, $executionId); + $form = $this->loadPage(); + $form->wait(1); + + if($form->dom->putoffSubmit === false) return $this->failed('错误的延期成功'); + if($dateType == 'end') + { + $info = sprintf($this->lang->execution->errorCommonEnd, ''); + $text = $form->dom->endTip->getText(); + } + else + { + $info = sprintf($this->lang->execution->errorCommonBegin, ''); + $text = $form->dom->beginTip->getText(); + } + /* 获取页面返回信息中除日期外的内容 */ + preg_match_all('/(\d{4}-\d{2}-\d{2})/', $text, $matches); ~ + $date = $matches[0][0]; ~ + $params = str_replace($date, '', $text); ~ + $params = trim($params); + if($params == $info) return $this->success('延期执行表单页提示信息正确'); + return $this->failed('延期执行表单页提示信息不正确'); + } + + /** + * 延期执行的可用工日不正确。 + * Putoff execution with wrong days. + * + * @param array $execution + * @param string $executionId + * @access public + * @return bool + */ + public function putoffWithWrongDays($execution, $executionId) + { + $this->inputFields($execution, $executionId); + $form = $this->loadPage(); + $form->wait(1); + if($form->dom->putoffSubmit === false) return $this->failed('错误的延期成功'); + if($form->dom->daysTip->getText() == $this->lang->project->copyProject->daysTips) return $this->success('延期执行表单页提示信息正确'); + return $this->failed('延期执行表单页提示信息不正确'); + } +} diff --git a/module/execution/test/lib/startexecution.ui.class.php b/module/execution/test/lib/startexecution.ui.class.php new file mode 100644 index 0000000000..465ff60215 --- /dev/null +++ b/module/execution/test/lib/startexecution.ui.class.php @@ -0,0 +1,60 @@ +initForm('execution', 'view', array('execution' => $executionId ), 'appIframe-execution'); + $form->wait(1); + $form->dom->btn($this->lang->execution->start)->click(); + $form->wait(1); + if(isset($realBegan)) $form->dom->realBegan->datePicker($realBegan); + $form->wait(1); + $form->dom->startSubmit->click(); + $form->wait(1); + } + + /** + * 正常开始执行。 + * Start execution. + * + * @param string $realBegan + * @access public + * @return bool + */ + public function start($realBegan) + { + $this->inputFields($realBegan); + $form = $this->loadPage(); + if($form->dom->status->getText() != $this->lang->execution->statusList->doing) return $this->failed('执行状态错误'); + if($form->dom->realBeganView->getText() != $realBegan) return $this->failed('执行实际开始日期错误'); + return $this->success('开始执行成功'); + } + + /** + * 实际开始日期大于当前日期。 + * The real start date is greater than the current date. + * + * @param string $realBegan + * @access public + * @return bool + */ + public function startWithGreaterDate($realBegan) + { + $this->inputFields($realBegan); + $form = $this->loadPage(); + $field = $form->dom->realBeganField->getText(); + $text = $form->dom->realBeganTip->getText(); + $info = sprintf($lang->error->le, $field, $realBegan); + if($text != $info) return $this->success('开始执行表单页提示信息正确'); + return $this->failed('开始执行表单页提示信息不正确'); + } +} diff --git a/module/execution/test/lib/suspendexecution.ui.class.php b/module/execution/test/lib/suspendexecution.ui.class.php new file mode 100644 index 0000000000..5c856e064b --- /dev/null +++ b/module/execution/test/lib/suspendexecution.ui.class.php @@ -0,0 +1,23 @@ +initForm('execution', 'view', array('execution' => $executionId ), 'appIframe-execution'); + $form->dom->btn($this->lang->execution->suspend)->click(); + $form->dom->suspendSubmit->click(); + $form->wait(1); + + if($form->dom->status->getText() != $this->lang->execution->statusList->suspended) return $this->failed('执行状态错误'); + return $this->success('挂起执行成功'); + } +} diff --git a/module/execution/test/ui/activateexecution.php b/module/execution/test/ui/activateexecution.php new file mode 100644 index 0000000000..51cdff71c4 --- /dev/null +++ b/module/execution/test/ui/activateexecution.php @@ -0,0 +1,38 @@ +loadYaml('execution', false, 2)->gen(1); +$execution = zenData('project'); +$execution->id->range('101,103'); +$execution->project->range('11'); +$execution->type->range('sprint'); +$execution->parent->range('11'); +$execution->path->range('`,11,101,`, `,11,103,`'); +$execution->grade->range('1'); +$execution->name->range('挂起的执行, 已关闭的执行'); +$execution->hasProduct->range('1'); +$execution->begin->range('(-1D)-(-3D):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->end->range('(+1D)-(+2D):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->days->range('10'); +$execution->status->range('suspended, closed'); +$execution->gen(2, false); + +$tester = new activateExecutionTester(); +$tester->login(); + +$end = array(date('Y-m-d', strtotime('+1 days')), '', date('Y-m-d', strtotime('-10 months')), date('Y-m-d', strtotime('+10 months'))); + +r($tester->activateWithLessEnd($end[1], '101')) && p('message') && e('激活执行表单页提示信息正确'); //计划完成日期为空,激活失败 +r($tester->activateWithLessEnd($end[2], '103')) && p('message') && e('激活执行表单页提示信息正确'); //计划完成日期小于计划开始日期,激活失败 +r($tester->activateWithGreaterEnd($end[3], '101')) && p('message') && e('激活执行表单页提示信息正确'); //计划完成日期大于项目的计划完成日期,激活失败 +r($tester->activate($end[0], '101')) && p('message') && e('激活执行成功'); //成功激活挂起的执行 +r($tester->activate($end[0], '103')) && p('message') && e('激活执行成功'); //成功激活已关闭的执行 +$tester->closeBrowser(); diff --git a/module/execution/test/ui/closeexecution.php b/module/execution/test/ui/closeexecution.php new file mode 100644 index 0000000000..707685229c --- /dev/null +++ b/module/execution/test/ui/closeexecution.php @@ -0,0 +1,37 @@ +#!/usr/bin/env php +loadYaml('execution', false, 2)->gen(10); +$execution = zenData('project'); +$execution->id->range('110'); +$execution->project->range('11'); +$execution->type->range('sprint'); +$execution->parent->range('11'); +$execution->path->range('`,11,110,`'); +$execution->grade->range('1'); +$execution->name->range('测试关闭执行'); +$execution->hasProduct->range('1'); +$execution->begin->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->end->range('(+2M)-(+3M):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->realBegan->range('(-2D)-(+D):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->status->range('doing'); +$execution->gen(1, false); + +$tester = new closeExecutionTester(); +$tester->login(); + +$realEnd = array(date('Y-m-d', strtotime('+20 days')), date('Y-m-d'), date('Y-m-d', strtotime('-4 days'))); + +r($tester->closeWithGreaterDate($realEnd[0])) && p('message') && e('关闭执行表单页提示信息正确'); +r($tester->close($realEnd[1])) && p('message') && e('关闭执行成功'); +r($tester->closeWithLessDate($realEnd[2])) && p('message') && e('关闭执行表单页提示信息正确'); +$tester->closeBrowser(); diff --git a/module/execution/test/ui/page/view.php b/module/execution/test/ui/page/view.php index 090bfb238f..cfe7fa3656 100755 --- a/module/execution/test/ui/page/view.php +++ b/module/execution/test/ui/page/view.php @@ -11,11 +11,25 @@ class viewPage extends page 'acl' => "//*[@id='mainContent']/div[1]/div[1]/div[2]/div[1]/span[3]", 'plannedBegin' => "//*[@id='mainContent']/div[2]/div[1]/div/table[3]/tbody/tr/td/div/div[1]/span[2]", 'plannedEnd' => "//*[@id='mainContent']/div[2]/div[1]/div/table[3]/tbody/tr/td/div/div[2]/span[2]", + 'realBeganView' => "//*[@id='mainContent']/div[2]/div[1]/div/table[3]/tbody/tr/td/div/div[3]/span[2]", + 'realEndView' => "//*[@id='mainContent']/div[2]/div[1]/div/table[3]/tbody/tr/td/div/div[4]/span[2]", 'edit' => "//*[@id='mainContent']/div[3]/div/a[last()-1]", /* 编辑执行弹窗中元素 */ 'products' => "//*[@name='products[0]']", 'productsTip' => "//*[@id='products[0]Tip']", 'submit' => "//*[@type='submit']/span", + /* 开始执行弹窗中的元素 */ + 'realBeganField' => "//div[@data-name='realBegan']/label/span", + 'startSubmit' => "//*[@name='realBegan']/../../../../div[3]/div/button", + /* 关闭执行弹窗中的元素 */ + 'realEndField' => "//div[@data-name='realEnd']/label/span", + 'closeSubmit' => "//*[@name='realEnd']/../../../../div[3]/div/button", + /* 挂起执行弹窗中的元素 */ + 'suspendSubmit' => "//*[@class='modal-actions']/../div[3]/div/div/form/div[2]/div/button", + /* 延期执行弹窗中的元素 */ + 'putoffSubmit' => "//*[@class='modal-actions']/../div[3]/div/div/form/div[4]/div/button", + /* 激活执行弹窗中的元素 */ + 'activateSubmit' => "//*[@class='modal-actions']/../div[3]/div/div/form/div[3]/div/button", ); $this->dom->xpath = array_merge($this->dom->xpath, $xpath); } diff --git a/module/execution/test/ui/putoffexecution.php b/module/execution/test/ui/putoffexecution.php new file mode 100644 index 0000000000..3f48ea7145 --- /dev/null +++ b/module/execution/test/ui/putoffexecution.php @@ -0,0 +1,97 @@ +id->range('1'); +$project->name->range('项目'); +$project->project->range('0'); +$project->model->range('scrum'); +$project->type->range('project'); +$project->parent->range('0'); +$project->auth->range('extend'); +$project->path->range('`,1,`'); +$project->grade->range('1'); +$project->hasProduct->range('1'); +$project->begin->range('(-2M)-(-M):1D')->type('timestamp')->format('YY/MM/DD'); +$project->end->range('(+2M)-(+3M):1D')->type('timestamp')->format('YY/MM/DD'); +$project->gen(1); + +$execution = zenData('project'); +$execution->id->range('101,103'); +$execution->project->range('1'); +$execution->type->range('sprint'); +$execution->parent->range('1'); +$execution->path->range('`,1,101,`, `,1,103,`'); +$execution->grade->range('1'); +$execution->name->range('未开始执行, 进行中执行'); +$execution->hasProduct->range('1'); +$execution->begin->range('(-1M)-(-3W):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->end->range('(+1M)-(+2M):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->days->range('10'); +$execution->realBegan->range('(-2w)-(-1w):1D')->type('timestamp')->format('YY/MM/DD'); +$execution->status->range('wait, doing'); +$execution->gen(2, false); + +$tester = new putoffexecutionTester(); +$tester->login(); + +$execution = array( + '0' => array( + 'days' => '10', + ), + '1' => array( + 'begin' => date('Y-m-d', strtotime('-1 days')), + 'end' => date('Y-m-d', strtotime('+1 days')), + 'days' => '10', + ), + '2' => array( + 'begin' => '', + 'end' => date('Y-m-d'), + 'days' => '10', + ), + '3' =>array( + 'begin' => date('Y-m-d'), + 'end' => '', + 'days' => '10', + ), + '4' => array( + 'begin' => date('Y-m-d', strtotime('-10 months')), + 'end' => date('Y-m-d'), + 'days' => '10', + ), + '5' => array( + 'begin' => date('Y-m-d'), + 'end' => date('Y-m-d', strtotime('+10 months')), + 'days' => '10', + ), + '6' => array( + 'begin' => date('Y-m-d'), + 'end' => date('Y-m-d'), + 'days' => '', + ), + '7' => array( + 'begin' => date('Y-m-d'), + 'end' => date('Y-m-d'), + 'days' => 'aaa', + ), +); + +r($tester->putoff($execution['0'], '101')) && p('message') && e('延期执行成功'); //未开始的执行,延期弹窗直接点击保存 +r($tester->putoff($execution['0'], '103')) && p('message') && e('延期执行成功'); //进行中的执行,延期弹窗直接点击保存 +r($tester->putoff($execution['1'], '101')) && p('message') && e('延期执行成功'); //未开始的执行,延期弹窗中修改正确的起止日期后点击保存 +r($tester->putoff($execution['1'], '103')) && p('message') && e('延期执行成功'); //进行中的执行,延期弹窗中修改正确的起止日期后点击保存 +r($tester->putoffWithWrongDate($execution['2'], '101', 'begin')) && p('message') && e('延期执行表单页提示信息正确'); //计划开始日期为空 +r($tester->putoffWithWrongDate($execution['3'], '103', 'end')) && p('message') && e('延期执行表单页提示信息正确'); //计划结束日期为空 +r($tester->putoffWithWrongDate($execution['4'], '103', 'begin')) && p('message') && e('延期执行表单页提示信息正确'); //计划开始日期小于项目计划开始日期 +r($tester->putoffWithWrongDate($execution['5'], '101', 'end')) && p('message') && e('延期执行表单页提示信息正确'); //计划结束日期大于项目计划完成日期 +r($tester->putoffWithWrongDays($execution['6'], '101')) && p('message') && e('延期执行表单页提示信息正确'); //可用工日为空 +r($tester->putoffWithWrongDays($execution['7'], '101')) && p('message') && e('延期执行表单页提示信息正确'); //可用工日为非数字 +$tester->closeBrowser(); diff --git a/module/execution/test/ui/startexecution.php b/module/execution/test/ui/startexecution.php new file mode 100644 index 0000000000..e323abecae --- /dev/null +++ b/module/execution/test/ui/startexecution.php @@ -0,0 +1,21 @@ +#!/usr/bin/env php +loadYaml('execution', false, 2)->gen(10); +$tester = new startExecutionTester(); +$tester->login(); + +$realBegan = array(date('Y-m-d', strtotime('+20 days')), date('Y-m-d')); + +r($tester->startWithGreaterDate($realBegan[0])) && p('message') && e('开始执行表单页提示信息正确'); +r($tester->start($realBegan[1])) && p('message') && e('开始执行成功'); +$tester->closeBrowser(); diff --git a/module/execution/test/ui/suspendexecution.php b/module/execution/test/ui/suspendexecution.php new file mode 100644 index 0000000000..c28049bc3e --- /dev/null +++ b/module/execution/test/ui/suspendexecution.php @@ -0,0 +1,19 @@ +#!/usr/bin/env php +loadYaml('execution', false, 2)->gen(10); +$tester = new suspendExecutionTester(); +$tester->login(); + +r($tester->suspend('101')) && p('message') && e('挂起执行成功'); //挂起未开始的执行 +r($tester->suspend('103')) && p('message') && e('挂起执行成功'); //挂起进行中的执行 +$tester->closeBrowser(); diff --git a/module/execution/ui/create.field.php b/module/execution/ui/create.field.php index 505b13abc4..4084309d3e 100644 --- a/module/execution/ui/create.field.php +++ b/module/execution/ui/create.field.php @@ -5,7 +5,6 @@ global $lang, $config; $fields = defineFieldList('execution.create'); $project = data('project'); $from = data('from'); -$hidden = empty($project->hasProduct) ? 'hidden' : ''; $isStage = isset($project->model) && in_array($project->model, array('waterfall', 'waterfallplus')); $isKanban = data('isKanban'); $showExecutionExec = ($from == 'execution' || $from == 'doc'); @@ -78,7 +77,6 @@ $fields->field('percent') $fields->field('productsBox') ->width('full') - ->className($hidden) ->control(array ( 'control' => 'productsBox', @@ -102,10 +100,10 @@ $fields->field('desc') ->label($showExecutionExec ? $lang->execution->execDesc : $lang->execution->desc) ->control(array('control' => 'editor', 'templateType' => 'execution')); -$fields->field('PO')->foldable()->required(strpos($requiredFields, ",PO,") !== false)->items(data('poUsers'))->value(data('execution.PO')); -$fields->field('QD')->foldable()->required(strpos($requiredFields, ",QD,") !== false)->items(data('qdUsers'))->value(data('execution.QD')); -$fields->field('PM')->foldable()->required(strpos($requiredFields, ",PM,") !== false)->items(data('pmUsers'))->value(data('execution.PM'))->label($showExecutionExec ? $lang->execution->execPM : $lang->execution->PM); -$fields->field('RD')->foldable()->required(strpos($requiredFields, ",RD,") !== false)->items(data('rdUsers'))->value(data('execution.RD')); +$fields->field('PO')->foldable()->required(strpos($requiredFields, ",PO,") !== false)->control(array('control' => 'picker', 'required' => false))->items(data('poUsers'))->value(data('execution.PO')); +$fields->field('QD')->foldable()->required(strpos($requiredFields, ",QD,") !== false)->control(array('control' => 'picker', 'required' => false))->items(data('qdUsers'))->value(data('execution.QD')); +$fields->field('PM')->foldable()->required(strpos($requiredFields, ",PM,") !== false)->control(array('control' => 'picker', 'required' => false))->items(data('pmUsers'))->value(data('execution.PM'))->label($showExecutionExec ? $lang->execution->execPM : $lang->execution->PM); +$fields->field('RD')->foldable()->required(strpos($requiredFields, ",RD,") !== false)->control(array('control' => 'picker', 'required' => false))->items(data('rdUsers'))->value(data('execution.RD')); $fields->field('teamName') ->foldable() diff --git a/module/execution/ui/edit.field.php b/module/execution/ui/edit.field.php index 56d17d39ed..ce03e62955 100644 --- a/module/execution/ui/edit.field.php +++ b/module/execution/ui/edit.field.php @@ -153,17 +153,23 @@ $fields->field('productsBox') 'from' => 'execution' )); -$fields->field('PO') - ->label($lang->execution->PO) - ->required(in_array('PO', explode(',', $config->execution->edit->requiredFields))) - ->items(data('poUsers')) - ->value(data('execution.PO')); +if($execution->attribute != 'review') +{ + $fields->field('PO') + ->label($lang->execution->PO) + ->required(in_array('PO', explode(',', $config->execution->edit->requiredFields))) + ->items(data('poUsers')) + ->value(data('execution.PO')); +} -$fields->field('QD') - ->label($lang->execution->QD) - ->required(in_array('QD', explode(',', $config->execution->edit->requiredFields))) - ->items(data('qdUsers')) - ->value(data('execution.QD')); +if(!in_array($execution->attribute, array('request', 'design', 'review'))) +{ + $fields->field('QD') + ->label($lang->execution->QD) + ->required(in_array('QD', explode(',', $config->execution->edit->requiredFields))) + ->items(data('qdUsers')) + ->value(data('execution.QD')); +} $fields->field('PM') ->label($lang->execution->PM) @@ -171,11 +177,14 @@ $fields->field('PM') ->items(data('pmUsers')) ->value(data('execution.PM')); -$fields->field('RD') - ->label($lang->execution->RD) - ->required(in_array('RD', explode(',', $config->execution->edit->requiredFields))) - ->items(data('rdUsers')) - ->value(data('execution.RD')); +if(!in_array($execution->attribute, array('request', 'design', 'review'))) +{ + $fields->field('RD') + ->label($lang->execution->RD) + ->required(in_array('RD', explode(',', $config->execution->edit->requiredFields))) + ->items(data('rdUsers')) + ->value(data('execution.RD')); +} $fields->field('team') ->width('full') diff --git a/module/execution/ui/importbug.html.php b/module/execution/ui/importbug.html.php index 05e2f3cd48..3dd8dde2a1 100644 --- a/module/execution/ui/importbug.html.php +++ b/module/execution/ui/importbug.html.php @@ -16,7 +16,14 @@ featureBar ( set::current('all'), set::linkParams("executionID={$execution->id}"), - isInModal() ? null : li(searchToggle(set::module('importBug'))) +); + +searchForm +( + set::module('importBug'), + set::simple(true), + set::show(true), + set::onSearch(jsRaw("window.onSearchLinks.bind(null, 'importBug')")) ); $footToolbar['items'][] = array( diff --git a/module/execution/ui/story.html.php b/module/execution/ui/story.html.php index c1e28bfd4a..3868cc71ab 100644 --- a/module/execution/ui/story.html.php +++ b/module/execution/ui/story.html.php @@ -40,9 +40,13 @@ featureBar set::width('150px'), setStyle('justify-content', 'center'), set::display($lang->story->viewAllGrades), - set::menu(array('checkbox' => true)), + set::menu(array('checkbox' => true, 'itemProps' => array('innerComponent' => 'a'))), set::value($showGrades), - set::onPopHidden(jsRaw('setShowGrades')) + set::toolbar + ( + array('text' => $lang->confirm, 'onClick' => jsRaw('(e,info) => {setShowGrades();info.relativeTarget.close();}')), + array('text' => $lang->cancel, 'onClick' => jsRaw('(e,info) => info.relativeTarget.close()')), + ) ) ), set::current($this->session->storyBrowseType), @@ -201,7 +205,7 @@ $product ? toolbar 'class' => 'ghost', 'url' => createLink('story', 'report', "productID={$product->id}&branchID=&storyType={$storyType}&browseType={$type}&moduleID={$param}&chartType=pie&projectID={$execution->id}") . "#app={$app->tab}" ))) : null, - hasPriv('story', 'export') && $type == 'byproduct' ? item(set(array + hasPriv('story', 'export') && ($linkedProductCount < 2 || $type == 'byproduct' || $type == 'bymodule') ? item(set(array ( 'text' => $lang->export, 'icon' => 'export', @@ -450,7 +454,8 @@ foreach($setting as $col) if($col['name'] == 'title') { - $col['link'] = createLink('execution', 'storyView', array('storyID' => '{id}', 'execution' => $execution->id)); + $tab = $execution->multiple ? 'execution' : 'project'; + $col['link'] = createLink('execution', 'storyView', array('storyID' => '{id}', 'execution' => $execution->id)) . "#app={$tab}"; $col['title'] = $this->lang->story->name; } diff --git a/module/execution/ui/taskkanban.html.php b/module/execution/ui/taskkanban.html.php index beb7ffb8cd..ec20980f59 100644 --- a/module/execution/ui/taskkanban.html.php +++ b/module/execution/ui/taskkanban.html.php @@ -36,6 +36,12 @@ foreach($kanbanGroup as $index => $group) if(isset($col['parent']) && $col['parent'] != '-1') $groupCols[$groupID][$col['id']] = $col['title']; } + if($execution->displayCards > 0) + { + $group['minLaneHeight'] = $execution->displayCards * 70; + $group['maxLaneHeight'] = $execution->displayCards * 70; + } + $kanbanGroup[$index] = $group; } $laneCount += count($kanbanGroup); diff --git a/module/execution/ui/view.html.php b/module/execution/ui/view.html.php index 5bef48f366..24779adb32 100644 --- a/module/execution/ui/view.html.php +++ b/module/execution/ui/view.html.php @@ -479,7 +479,7 @@ div common::hasPriv('execution', 'manageproducts') && $execution->type != 'stage' && $project->model != 'waterfallplus' ? btn ( setClass('ghost text-gray font-normal'), - set::icon('link text-primary'), + set::trailingIcon('caret-right pb-0.5'), set::url(createLink('execution', 'manageproducts', "projectID={$execution->id}")), $lang->more ) : null diff --git a/module/execution/zen.php b/module/execution/zen.php index 5b226820a4..5c165da303 100644 --- a/module/execution/zen.php +++ b/module/execution/zen.php @@ -754,7 +754,7 @@ class executionZen extends execution if(!empty($this->config->limitTaskDate)) { - $this->task->checkEstStartedAndDeadline($executionID, $task->estStarted, $task->deadline); + $this->task->checkEstStartedAndDeadline($execution->id, (string)$task->estStarted, (string)$task->deadline); if(dao::isError()) return false; } diff --git a/module/extension/model.php b/module/extension/model.php index 4a3134c4a5..11657fd180 100644 --- a/module/extension/model.php +++ b/module/extension/model.php @@ -1,7 +1,7 @@ file->mimes['default'] = 'application/octet-stream'; $config->file->imageExtensions = array('jpeg', 'jpg', 'gif', 'png'); $config->file->image2Compress = array('.jpg', '.bmp', '.jpeg'); +$config->file->img2href = array('desc', 'steps'); + $config->file->charset = array('UTF-8' => 'UTF-8', 'GBK' => 'GBK', 'BIG5' => 'BIG5'); $config->file->maxImport = 100; diff --git a/module/file/control.php b/module/file/control.php index f7a8840b2a..d71c3c76ea 100755 --- a/module/file/control.php +++ b/module/file/control.php @@ -179,6 +179,7 @@ class file extends control { $fields = $this->post->fields; $rows = $this->post->rows; + $domain = common::getSysURL(); /* Build csv content. */ $output = ''; @@ -190,6 +191,14 @@ class file extends control $output .= '"'; foreach($fields as $fieldName => $fieldLabel) { + /* 标签替换成链接。 */ + if(in_array($fieldName, $this->config->file->img2href)) + { + $pattern = '/([^/'; + $replacement = '$1 (' . $domain . '/$2)'; + $row->$fieldName = preg_replace($pattern, $replacement, $row->$fieldName); + } + $output .= isset($row->$fieldName) ? str_replace(array('"', ' ', '>'), array('“', ' ', '>'), htmlSpecialString(strip_tags((string)$row->$fieldName, ''))) : ''; $output .= '","'; } diff --git a/module/file/ui/download.html.php b/module/file/ui/download.html.php index 8494c0ced0..ad12776a23 100644 --- a/module/file/ui/download.html.php +++ b/module/file/ui/download.html.php @@ -55,9 +55,10 @@ else ) : null ); + $fileContent = null; if($fileType == 'image') { - div + $fileContent = div ( setID('imageFile'), h::img(set::src($this->createLink('file', 'read', "fileID={$file->id}"))) @@ -65,26 +66,28 @@ else } elseif($fileType == 'video') { - div + $fileContent = div ( setID('videoFile'), h::video(set::src($file->webPath), set::controls(true), set::autoplay(true), set::controlsList('nodownload'), set::onerror('showError()'), set::onloadedmetadata('loadedmetadata()'), set::style(array('width' => '100%'))), div ( - setClass('playfailed hide'), + setClass('playfailed hidden'), $lang->file->playFailed ) ); } else { - div + $fileContent = div ( setID('txtFile'), h::pre(set::style(array('background-color' => 'rgb(var(--color-gray-200-rgb))')), $fileContent) ); } + div(setClass('panel-form'), $fileContent); + $isInModal = isInModal(); h::js ( @@ -101,7 +104,7 @@ window.setCharset = function(obj) function showError() { - $('.playfailed').show(); + $('.playfailed').removeClass('hidden'); } function loadedmetadata() diff --git a/module/file/ui/export.html.php b/module/file/ui/export.html.php index cb92283936..8e30dbde4f 100644 --- a/module/file/ui/export.html.php +++ b/module/file/ui/export.html.php @@ -24,6 +24,7 @@ $isCustomExport = (!empty($customExport) and !empty($allExportFields)); $showBizGuide = $config->edition == 'open' && empty($config->{$this->moduleName}->closeBizGuide) ? true : false; $bizGuideLink = common::checkNotCN() ? 'https://www.zentao.pm/page/zentao-pricing.html' : 'https://www.zentao.net/page/enterprise.html'; $bizName = $showBizGuide ? "{$lang->bizName}" : ''; +$defaultExportFields = ''; if($isCustomExport) { @@ -274,6 +275,9 @@ window.onChangeFileType = function(event) encodePicker.$.setValue('utf-8'); encodePicker.render({disabled: true}); + + $('#tplBox').toggleClass('hidden', fileType == 'word'); + $('.customFieldsBox').toggleClass('hidden', fileType == 'word' || !$('#showCustomFieldsBox').prop('checked')); } window.onChangeFileName = function(event) diff --git a/module/group/packagemanager.php b/module/group/packagemanager.php index 3b1f812489..69c39aa3f0 100644 --- a/module/group/packagemanager.php +++ b/module/group/packagemanager.php @@ -1598,24 +1598,25 @@ $config->group->package->manageFeedback = new stdclass(); $config->group->package->manageFeedback->order = 10; $config->group->package->manageFeedback->subset = 'feedback'; $config->group->package->manageFeedback->privs = array(); -$config->group->package->manageFeedback->privs['feedback-create'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 0, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-ask', 'feedback-assignTo', 'feedback-close', 'feedback-comment', 'feedback-edit')); -$config->group->package->manageFeedback->privs['feedback-edit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 1, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-ask', 'feedback-assignTo', 'feedback-batchEdit', 'feedback-close', 'feedback-comment', 'feedback-create')); -$config->group->package->manageFeedback->privs['feedback-assignTo'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 4, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-ask', 'feedback-batchAssignTo', 'feedback-close', 'feedback-comment', 'feedback-create', 'feedback-edit')); -$config->group->package->manageFeedback->privs['feedback-comment'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 5, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-ask', 'feedback-assignTo', 'feedback-close', 'feedback-create', 'feedback-edit')); -$config->group->package->manageFeedback->privs['feedback-ask'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or', 'order' => 6, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-assignTo', 'feedback-close', 'feedback-comment', 'feedback-create', 'feedback-edit')); -$config->group->package->manageFeedback->privs['feedback-close'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 8, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-ask', 'feedback-assignTo', 'feedback-batchClose', 'feedback-comment', 'feedback-create', 'feedback-edit')); -$config->group->package->manageFeedback->privs['feedback-activate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or', 'order' => 3, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-ask', 'feedback-assignTo', 'feedback-close', 'feedback-comment', 'feedback-create', 'feedback-edit')); -$config->group->package->manageFeedback->privs['feedback-batchEdit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or', 'order' => 2, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-edit')); -$config->group->package->manageFeedback->privs['feedback-batchClose'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or', 'order' => 9, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-close')); -$config->group->package->manageFeedback->privs['feedback-batchAssignTo'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or', 'order' => 7, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-assignTo')); +$config->group->package->manageFeedback->privs['feedback-create'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 0, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-ask', 'feedback-assignTo', 'feedback-close', 'feedback-comment', 'feedback-edit')); +$config->group->package->manageFeedback->privs['feedback-batchCreate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 1, 'depend' => array('feedback-admin', 'feedback-create'), 'recommend' => array('feedback-adminView', 'feedback-edit')); +$config->group->package->manageFeedback->privs['feedback-edit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 2, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-ask', 'feedback-assignTo', 'feedback-batchEdit', 'feedback-close', 'feedback-comment', 'feedback-create')); +$config->group->package->manageFeedback->privs['feedback-assignTo'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 5, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-ask', 'feedback-batchAssignTo', 'feedback-close', 'feedback-comment', 'feedback-create', 'feedback-edit')); +$config->group->package->manageFeedback->privs['feedback-comment'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 6, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-ask', 'feedback-assignTo', 'feedback-close', 'feedback-create', 'feedback-edit')); +$config->group->package->manageFeedback->privs['feedback-ask'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 7, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-assignTo', 'feedback-close', 'feedback-comment', 'feedback-create', 'feedback-edit')); +$config->group->package->manageFeedback->privs['feedback-close'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 9, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-activate', 'feedback-ask', 'feedback-assignTo', 'feedback-batchClose', 'feedback-comment', 'feedback-create', 'feedback-edit')); +$config->group->package->manageFeedback->privs['feedback-activate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 4, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-ask', 'feedback-assignTo', 'feedback-close', 'feedback-comment', 'feedback-create', 'feedback-edit')); +$config->group->package->manageFeedback->privs['feedback-batchEdit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 3, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-edit')); +$config->group->package->manageFeedback->privs['feedback-batchClose'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 10, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-close')); +$config->group->package->manageFeedback->privs['feedback-batchAssignTo'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 8, 'depend' => array('feedback-admin'), 'recommend' => array('feedback-assignTo')); $config->group->package->importFeedback = new stdclass(); $config->group->package->importFeedback->order = 15; $config->group->package->importFeedback->subset = 'feedback'; $config->group->package->importFeedback->privs = array(); $config->group->package->importFeedback->privs['feedback-export'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 0, 'depend' => array('feedback-admin'), 'recommend' => array()); -$config->group->package->importFeedback->privs['feedback-import'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or', 'order' => 2, 'depend' => array('feedback-admin', 'feedback-exportTemplate'), 'recommend' => array()); -$config->group->package->importFeedback->privs['feedback-exportTemplate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or', 'order' => 1, 'depend' => array('feedback-admin', 'feedback-import'), 'recommend' => array()); +$config->group->package->importFeedback->privs['feedback-import'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 2, 'depend' => array('feedback-admin', 'feedback-exportTemplate'), 'recommend' => array()); +$config->group->package->importFeedback->privs['feedback-exportTemplate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,or,lite', 'order' => 1, 'depend' => array('feedback-admin', 'feedback-import'), 'recommend' => array()); $config->group->package->handleFeedback = new stdclass(); $config->group->package->handleFeedback->order = 20; @@ -1656,18 +1657,19 @@ $config->group->package->manageTicket->order = 10; $config->group->package->manageTicket->subset = 'ticket'; $config->group->package->manageTicket->privs = array(); $config->group->package->manageTicket->privs['ticket-create'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 0, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-activate', 'ticket-close', 'ticket-edit')); -$config->group->package->manageTicket->privs['ticket-edit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 1, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-activate', 'ticket-close', 'ticket-create')); -$config->group->package->manageTicket->privs['ticket-assignTo'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 3, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-batchAssignTo', 'ticket-create', 'ticket-edit')); -$config->group->package->manageTicket->privs['ticket-createBug'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 5, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-createStory', 'ticket-finish', 'ticket-start')); -$config->group->package->manageTicket->privs['ticket-createStory'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-createBug', 'ticket-finish', 'ticket-start')); -$config->group->package->manageTicket->privs['ticket-start'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-activate', 'ticket-assignTo', 'ticket-close', 'ticket-create', 'ticket-createBug', 'ticket-createStory', 'ticket-edit', 'ticket-finish')); -$config->group->package->manageTicket->privs['ticket-finish'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-createBug', 'ticket-createStory', 'ticket-start')); -$config->group->package->manageTicket->privs['ticket-close'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 10, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-edit')); +$config->group->package->manageTicket->privs['ticket-edit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 2, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-activate', 'ticket-close', 'ticket-create')); +$config->group->package->manageTicket->privs['ticket-assignTo'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 4, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-batchAssignTo', 'ticket-create', 'ticket-edit')); +$config->group->package->manageTicket->privs['ticket-createBug'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 6, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-createStory', 'ticket-finish', 'ticket-start')); +$config->group->package->manageTicket->privs['ticket-createStory'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 7, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-createBug', 'ticket-finish', 'ticket-start')); +$config->group->package->manageTicket->privs['ticket-start'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 8, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-activate', 'ticket-assignTo', 'ticket-close', 'ticket-create', 'ticket-createBug', 'ticket-createStory', 'ticket-edit', 'ticket-finish')); +$config->group->package->manageTicket->privs['ticket-finish'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 9, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-createBug', 'ticket-createStory', 'ticket-start')); +$config->group->package->manageTicket->privs['ticket-close'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 11, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-edit')); $config->group->package->manageTicket->privs['ticket-activate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 12, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-batchActivate')); -$config->group->package->manageTicket->privs['ticket-batchEdit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 2, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-edit')); -$config->group->package->manageTicket->privs['ticket-batchActivate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 13, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-activate')); -$config->group->package->manageTicket->privs['ticket-batchFinish'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 9, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-finish')); -$config->group->package->manageTicket->privs['ticket-batchAssignTo'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 4, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-assignTo')); +$config->group->package->manageTicket->privs['ticket-batchCreate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 1, 'depend' => array('ticket-browse','ticket-create'), 'recommend' => array('ticket-view','ticket-edit')); +$config->group->package->manageTicket->privs['ticket-batchEdit'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 3, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-edit')); +$config->group->package->manageTicket->privs['ticket-batchActivate'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 13, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-activate')); +$config->group->package->manageTicket->privs['ticket-batchFinish'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 10, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-finish')); +$config->group->package->manageTicket->privs['ticket-batchAssignTo'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite', 'order' => 5, 'depend' => array('ticket-browse'), 'recommend' => array('ticket-assignTo')); $config->group->package->manageTicket->privs['ticket-syncProduct'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd', 'order' => 14, 'depend' => array('ticket-browse'), 'recommend' => array()); $config->group->package->importTicket = new stdclass(); @@ -2529,8 +2531,11 @@ $config->group->package->workflowLayout = new stdclass(); $config->group->package->workflowLayout->order = 20; $config->group->package->workflowLayout->subset = 'workflow'; $config->group->package->workflowLayout->privs = array(); -$config->group->package->workflowLayout->privs['workflowlayout-admin'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 5, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); -$config->group->package->workflowLayout->privs['workflowlayout-block'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 10, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); +$config->group->package->workflowLayout->privs['workflowlayout-admin'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 5, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); +$config->group->package->workflowLayout->privs['workflowlayout-block'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 10, 'depend' => array('workflow-browseFlow'), 'recommend' => array()); +$config->group->package->workflowLayout->privs['workflowlayout-addUI'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 10, 'depend' => array('workflow-browseFlow', 'workflowlayout-admin'), 'recommend' => array('workflowlayout-editUI', 'workflowlayout-deleteUI')); +$config->group->package->workflowLayout->privs['workflowlayout-editUI'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 10, 'depend' => array('workflow-browseFlow', 'workflowlayout-admin'), 'recommend' => array('workflowlayout-addUI', 'workflowlayout-deleteUI')); +$config->group->package->workflowLayout->privs['workflowlayout-deleteUI'] = array('edition' => 'biz,max,ipd', 'vision' => 'rnd,lite,or', 'order' => 10, 'depend' => array('workflow-browseFlow', 'workflowlayout-admin'), 'recommend' => array('workflowlayout-editUI', 'workflowlayout-addUI')); $config->group->package->workflowCondition = new stdclass(); $config->group->package->workflowCondition->order = 25; diff --git a/module/host/model.php b/module/host/model.php index b2b503a0a5..de9ad53976 100644 --- a/module/host/model.php +++ b/module/host/model.php @@ -1,7 +1,7 @@ info.event.preventDefault() - } - ); + if(apps.openedMenu) apps.openedMenu.hide(); + apps.openedMenu = zui.ContextMenu.show({element: $btn[0], placement: $btn.closest('#appTabs').length ? 'top-start' : 'right-start', items: items, event: event, onClickItem: function(info){info.event.preventDefault();}, onHide: () => {apps.openedMenu = null}}); event.preventDefault(); }); diff --git a/module/instance/test/lib/instance.ui.class.php b/module/instance/test/lib/instance.ui.class.php new file mode 100644 index 0000000000..8013aeca5b --- /dev/null +++ b/module/instance/test/lib/instance.ui.class.php @@ -0,0 +1,61 @@ +login(); + $form = $this->initForm('space', 'browse', '', 'appIframe-devops'); + $elementCount = $this->page->webdriver->driver->findElement(WebDriverBy::xpath("(//div[text()='$type']/..)[1]"))->getAttribute('data-row'); + $this->page->webdriver->driver->findElement(WebDriverBy::xpath("//div[@data-col='name' and @data-row=$elementCount]//a[text()]"))->click(); + $this->webdriver->wait(1); + $newPage = $this->loadPage('instance', 'view'); + $buttonList = $this->page->webdriver->driver->findElements(WebDriverBy::xpath($newPage->dom->xpath['copyButton'])); + if($type == 'GitLab' || $type == 'SonarQube') + { + $form->dom->btn($this->lang->instance->management)->click(); + $this->webdriver->wait(1); + $search = $form->dom->btn($this->lang->searchAB)->getText(); + if($search == '搜索') return $this->success($type . '详情页无误'); + return $this->failed($type . '详情页有误,请检查'); + } + else + { + if($this->instanceViewAssert($newPage, $buttonList)) return $this->success($type . '详情页无误'); + return $this->failed($type . '详情页有误,请检查'); + } + } + + /** + * 应用详情页添加断言。 + * instance view assert. + * + * @param object $page + * @param array $buttonCount + * @access public + * @return bool + */ + public function instanceViewAssert(object $page, array $buttonList) + { + $toastList = []; + foreach($buttonList as $element) + { + $element->click(); + $this->webdriver->wait(1); + $toast = $page->dom->toast->getText(); + if($toast == '复制成功') $toastList[] = $toast; + } + if(count($toastList) == count($buttonList)) return true; + return false; + } +} diff --git a/module/instance/test/ui/instanceview.php b/module/instance/test/ui/instanceview.php new file mode 100644 index 0000000000..2522c5adc0 --- /dev/null +++ b/module/instance/test/ui/instanceview.php @@ -0,0 +1,25 @@ +loadYaml('pipeline')->gen(7); +$type = 'GitFox'; +r($tester->instanceView($type)) && p('message,status') && e('检查GitFox应用成功,SUCCESS'); //检查GitFox应用页面是否可以正常展示 + +$type = 'GitLab'; +r($tester->instanceView($type)) && p('message,status') && e('检查GitLab应用成功,SUCCESS'); //检查GitLab应用页面是否可以正常展示 + +$type = 'Gogs'; +r($tester->instanceView($type)) && p('message,status') && e('检查Gogs应用成功,SUCCESS'); //检查Gogs应用页面是否可以正常展示 + +$type = 'Gitea'; +r($tester->instanceView($type)) && p('message,status') && e('检查Gitea应用成功,SUCCESS'); //检查Gitea应用页面是否可以正常展示 + +$type = 'Jenkins'; +r($tester->instanceView($type)) && p('message,status') && e('检查Jenkins应用成功,SUCCESS'); //检查Jenkins应用页面是否可以正常展示 + +$type = 'SonarQube'; +r($tester->instanceView($type)) && p('message,status') && e('检查SonarQube应用成功,SUCCESS'); //检查SonarQube应用页面是否可以正常展示 + +$type = 'Nexus'; +r($tester->instanceView($type)) && p('message,status') && e('检查Nexus应用成功,SUCCESS'); //检查Nexus应用页面是否可以正常展示 diff --git a/module/instance/test/ui/page/view.php b/module/instance/test/ui/page/view.php new file mode 100644 index 0000000000..cf331f8b18 --- /dev/null +++ b/module/instance/test/ui/page/view.php @@ -0,0 +1,14 @@ + "//div[@id='instanceInfoContainer']//button", + 'toast' => "//div[text()='复制成功']" + ); + + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/job/control.php b/module/job/control.php index 159f162bd7..59fddaec7a 100644 --- a/module/job/control.php +++ b/module/job/control.php @@ -120,7 +120,7 @@ class job extends control $this->view->repo = $repoID ? $this->repo->getByID($repoID) : null; $this->view->products = array(0 => '') + $this->loadModel('product')->getProductPairsByProject($this->projectID); $this->view->jenkinsServerList = $this->loadModel('pipeline')->getPairs('jenkins'); - $this->view->sonarqubeServerList = $this->pipeline->getPairs('sonarqube'); + $this->view->sonarqubeServerList = array('' => '') + $this->pipeline->getPairs('sonarqube'); $this->display(); } @@ -179,7 +179,7 @@ class job extends control $this->view->repo = $repo; $this->view->products = $products; $this->view->jenkinsServerList = $this->loadModel('pipeline')->getPairs('jenkins'); - $this->view->sonarqubeServerList = $this->pipeline->getPairs('sonarqube'); + $this->view->sonarqubeServerList = array('' => '') + $this->pipeline->getPairs('sonarqube'); $this->display(); } @@ -380,6 +380,12 @@ class job extends control public function ajaxCheckSonarqubeLink(int $repoID, int $jobID = 0) { $repo = $this->loadModel('job')->getSonarqubeByRepo(array($repoID), $jobID, true); + + foreach($repo as $linkRepo) + { + if($linkRepo->id == $jobID) $this->send(array('result' => 'success', 'message' => '')); + } + if(!empty($repo)) { $message = $repo[$repoID]->deleted ? $this->lang->job->jobIsDeleted : sprintf($this->lang->job->repoExists, $repo[$repoID]->id . '-' . $repo[$repoID]->name); diff --git a/module/job/js/common.ui.js b/module/job/js/common.ui.js index 6ad0995e8f..0a2b07e444 100644 --- a/module/job/js/common.ui.js +++ b/module/job/js/common.ui.js @@ -72,8 +72,9 @@ window.changeTrigger = function(event) if(useZentao === '1') { + const frame = $('[name=frame]').val(); $('.job-form #paramDiv').show(); - $('.job-form .sonarqube').show(); + if(frame == 'sonarqube') $('.job-form .sonarqube').removeClass('hidden'); $('.job-form .custom-fields').show(); $('.job-form .comment-fields').css('display', 'flex'); $('.job-form [data-name="triggerType"]').show(); @@ -81,7 +82,7 @@ window.changeTrigger = function(event) else { $('.job-form #paramDiv').hide(); - $('.job-form .sonarqube').hide(); + $('.job-form .sonarqube').addClass('hidden'); $('.job-form .custom-fields').hide(); $('.job-form .comment-fields').hide(); $('.job-form [data-name="triggerType"]').hide(); @@ -96,10 +97,11 @@ window.checkSonarquebLink = function() { const repoID = $('[name=repo]').val(); const frame = $('[name=frame]').val(); + const jobID = typeof(job) == 'undefined' ? 0 : job.id; if(frame != 'sonarqube' || repoID == 0) return false; - $.getJSON($.createLink('job', 'ajaxCheckSonarqubeLink', 'repoID=' + repoID), function(result) + $.getJSON($.createLink('job', 'ajaxCheckSonarqubeLink', 'repoID=' + repoID + '&jobID=' + jobID), function(result) { if(result.result != 'success') zui.Modal.alert(result.message); }) @@ -145,7 +147,7 @@ window.changeTriggerType = function(event) window.changeSonarqubeServer = function(event) { - const sonarqubeID = $(event.target).val(); + const sonarqubeID = $('[name=sonarqubeServer]').val(); $.get($.createLink('sonarqube', 'ajaxGetProjectList', 'sonarqubeID=' + sonarqubeID), function(data) { data = JSON.parse(data); @@ -158,13 +160,14 @@ window.changeSonarqubeServer = function(event) window.changeFrame = function(event) { - const frame = $(event.target).val(); + const frame = $(event.target).val(); + const useZentao = $('[name=useZentao]:checked').val(); if(frame == 'sonarqube') { - $('div.sonarqube').removeClass('hidden'); - /* Check exists sonarqube data. */ checkSonarquebLink(); + + if(useZentao == '1') $('div.sonarqube').removeClass('hidden'); } else { diff --git a/module/job/lang/zh-cn.php b/module/job/lang/zh-cn.php index b2c3293560..ba1096dbf0 100644 --- a/module/job/lang/zh-cn.php +++ b/module/job/lang/zh-cn.php @@ -59,7 +59,7 @@ $lang->job->cronSample = '如 0 0 2 * * 2-6/1 表示每个工作日凌晨2 $lang->job->sendExec = '发送执行请求成功!执行结果:%s'; $lang->job->inputName = '请输入参数名称。'; $lang->job->invalidName = '参数名称应该是英文字母、数字或下划线的组合。'; -$lang->job->repoExists = '此版本库已关联构建任务『%s』'; +$lang->job->repoExists = '此代码库已关联流水线『%s』'; $lang->job->projectExists = '此SonarQube项目已关联构建任务『%s』'; $lang->job->mustUseJenkins = 'SonarQube工具/框架仅在构建引擎为JenKins的情况下使用'; $lang->job->jobIsDeleted = '此版本库已关联构建任务,请从回收站查看数据'; diff --git a/module/job/model.php b/module/job/model.php index 948c3f7cb0..ccdc16fd6b 100644 --- a/module/job/model.php +++ b/module/job/model.php @@ -1,7 +1,7 @@ call('window.changeSonarqubeServer'), set::actionsClass('w-2/3'), formGroup ( @@ -177,11 +178,9 @@ formPanel ( set::name('sonarqubeServer'), set::label($lang->job->sonarqubeServer), - set::items($sonarqubeServerList), - set::value(''), set::width('1/2'), + set::items($sonarqubeServerList), set::required(true), - on::change('changeSonarqubeServer') ) ), formRow diff --git a/module/job/ui/edit.html.php b/module/job/ui/edit.html.php index a3040117c4..bd4daa7cec 100644 --- a/module/job/ui/edit.html.php +++ b/module/job/ui/edit.html.php @@ -268,7 +268,7 @@ formPanel set::name('sonarqubeServer'), set::label($lang->job->sonarqubeServer), set::width('1/2'), - set::items(array('' => '') +$sonarqubeServerList), + set::items($sonarqubeServerList), set::value($job->sonarqubeServer), set::required(true), on::change('changeSonarqubeServer') diff --git a/module/kanban/control.php b/module/kanban/control.php index 5f54276916..7b18eec75d 100644 --- a/module/kanban/control.php +++ b/module/kanban/control.php @@ -1758,4 +1758,18 @@ class kanban extends control } return print(json_encode($itemList)); } + + /** + * 获取空间的团队成员。 + * Ajax get space team memebers. + * + * @param int $spaceID + * @access public + * @return void + */ + public function ajaxGetSpaceTeam(int $spaceID) + { + $space = $this->loadModel('kanban')->getSpaceById($spaceID); + return print(json_encode($space->team)); + } } diff --git a/module/kanban/js/common.ui.js b/module/kanban/js/common.ui.js index 6ddf2f8cc0..46c7b330be 100644 --- a/module/kanban/js/common.ui.js +++ b/module/kanban/js/common.ui.js @@ -39,11 +39,11 @@ window.changeKanbanType = function() const url = $.createLink('kanban', 'create', 'spaceID=' + spaceID + '&type=' + type); loadPartial(url, '#WIPCountBox, #spaceBox, #nameBox, #ownerBox, #teamBox, #fixedColBox, #autoColBox, #archiveBox, #manageProgressBox, #alignmentBox, #descBox, #whitelistBox', {success: function() { - waitDom('#spaceBox [name=space]', function() + setTimeout(function() { const copySpaceID = $('#spaceBox input[name=space]').val(); $('#spaceBox input[name=space]').zui('picker').$.setValue(copySpaceID); - }); + }, 10) }}); } @@ -69,12 +69,21 @@ window.changeKanbanSpace = function() } else { - url = $.createLink('kanban', 'ajaxLoadUsers', 'spaceID=' + spaceID + '&field=owner'); + const url = $.createLink('kanban', 'ajaxLoadUsers', 'spaceID=' + spaceID + '&field=owner'); $.getJSON(url, function(data) { $('[name=owner]').zui('picker').render({items: data}); }); } + + if(config.currentMethod == 'space') + { + const getTeamUrl = $.createLink('kanban', 'ajaxGetSpaceTeam', 'spaceID=' + spaceID); + $.getJSON(getTeamUrl, function(team) + { + $('[name^=team]').zui('picker').$.setValue(team) + }); + } } /** diff --git a/module/kanban/ui/create.html.php b/module/kanban/ui/create.html.php index 7c8136ac6c..11159a4509 100644 --- a/module/kanban/ui/create.html.php +++ b/module/kanban/ui/create.html.php @@ -129,7 +129,9 @@ formPanel set::name('team'), set::items($users), set::multiple(true), - set::value(isset($copyKanban->team) ? $copyKanban->team : '') + set::value(isset($copyKanban->team) ? $copyKanban->team : $spaceTeam), + set::menu(array('checkbox' => true)), + set::toolbar(true) ) ) ), diff --git a/module/kanban/ui/space.html.php b/module/kanban/ui/space.html.php index 48a76f7f84..fff81948cb 100644 --- a/module/kanban/ui/space.html.php +++ b/module/kanban/ui/space.html.php @@ -107,10 +107,16 @@ foreach($spaceList as $space) $space->type == 'cooperation' && $kanban->owner == $this->app->user->account ? cell(set::className('label text-important ring-important mx-1'), setStyle(array('min-width' => '44px')), $lang->kanban->mine) : null, $cardActions ? cell ( - set::className('flex-1 text-right'), + set::className('flex-1 text-right card-settings'), dropdown ( - btn(setClass('btn dropdown-toggle ghost'), set::icon('ellipsis-v'), set::caret(false)), + btn + ( + on::click()->prevent(), + setClass('btn dropdown-toggle ghost'), + set::icon('ellipsis-v'), + set::caret(false) + ), set::items($cardActions) ) ) : null @@ -139,7 +145,5 @@ foreach($spaceList as $space) ); } -!empty($spaceList) ? $blocks : div(set::className('dtable'), div(set::className('dtable-empty-tip'), span(set::className('text-gray'), $lang->kanbanspace->empty))); -if(!empty($spaceList)) div(set::className('table-footer'), pager(set(usePager()), set::className('pull-right'))); - -render(); +!empty($spaceList) ? $blocks : div(set::className('dtable'), setKey('empty-tip'), div(set::className('dtable-empty-tip'), span(set::className('text-gray'), $lang->kanbanspace->empty))); +if(!empty($spaceList)) div(set::className('table-footer'), setKey('pager'), pager(set(usePager()), set::className('pull-right'))); diff --git a/module/kanban/zen.php b/module/kanban/zen.php index 227cf5930c..e317071601 100644 --- a/module/kanban/zen.php +++ b/module/kanban/zen.php @@ -58,6 +58,7 @@ class kanbanZen extends kanban $this->view->enableImport = $enableImport; $this->view->importObjects = $importObjects; $this->view->copyRegion = isset($output['copyRegion']) ? 1 : 0; + $this->view->spaceTeam = !empty($space->team) ? $space->team : ''; $this->display(); } diff --git a/module/mail/config.php b/module/mail/config.php index 16d3e0b39b..5312887b04 100644 --- a/module/mail/config.php +++ b/module/mail/config.php @@ -14,7 +14,7 @@ $config->mail->smtp->charset = 'utf-8'; // Charset $config->mail->smtp->auth = true; // Need auth or not. true|false $config->mail->smtp->host = 'localhost';// The smtp server host address. $config->mail->smtp->port = '25'; // The smtp server host port. -$config->mail->smtp->secure = ''; // The type to encode datas, 'ssl' or 'tls' allowed +$config->mail->smtp->secure = 0; // The type to encode datas, 'ssl' or 'tls' allowed $config->mail->smtp->username = ''; // The smtp user, may be a full email adress. $config->mail->smtp->password = ''; // The smtp user's password. diff --git a/module/mail/lang/de.php b/module/mail/lang/de.php index 1f562d5461..126971be28 100644 --- a/module/mail/lang/de.php +++ b/module/mail/lang/de.php @@ -72,7 +72,7 @@ $lang->mail->debugList[2] = 'Hoch'; $lang->mail->authList[1] = 'Erforderlich'; $lang->mail->authList[0] = 'Optional'; -$lang->mail->secureList[''] = 'Ohne'; +$lang->mail->secureList['0'] = 'Ohne'; $lang->mail->secureList['ssl'] = 'SSL'; $lang->mail->secureList['tls'] = 'TLS'; diff --git a/module/mail/lang/en.php b/module/mail/lang/en.php index fd5e69393e..7eece71c15 100755 --- a/module/mail/lang/en.php +++ b/module/mail/lang/en.php @@ -72,7 +72,7 @@ $lang->mail->debugList[2] = 'High'; $lang->mail->authList[1] = 'Yes'; $lang->mail->authList[0] = 'No'; -$lang->mail->secureList[''] = 'Plain'; +$lang->mail->secureList['0'] = 'Plain'; $lang->mail->secureList['ssl'] = 'ssl'; $lang->mail->secureList['tls'] = 'tls'; diff --git a/module/mail/lang/fr.php b/module/mail/lang/fr.php index fd5e69393e..7eece71c15 100644 --- a/module/mail/lang/fr.php +++ b/module/mail/lang/fr.php @@ -72,7 +72,7 @@ $lang->mail->debugList[2] = 'High'; $lang->mail->authList[1] = 'Yes'; $lang->mail->authList[0] = 'No'; -$lang->mail->secureList[''] = 'Plain'; +$lang->mail->secureList['0'] = 'Plain'; $lang->mail->secureList['ssl'] = 'ssl'; $lang->mail->secureList['tls'] = 'tls'; diff --git a/module/mail/lang/zh-cn.php b/module/mail/lang/zh-cn.php index 454fe7e55d..3a8a3919c2 100755 --- a/module/mail/lang/zh-cn.php +++ b/module/mail/lang/zh-cn.php @@ -72,7 +72,7 @@ $lang->mail->debugList[2] = '较高'; $lang->mail->authList[1] = '需要'; $lang->mail->authList[0] = '不需要'; -$lang->mail->secureList[''] = '不加密'; +$lang->mail->secureList['0'] = '不加密'; $lang->mail->secureList['ssl'] = 'ssl'; $lang->mail->secureList['tls'] = 'tls'; diff --git a/module/mail/model.php b/module/mail/model.php index 2a95c7069a..0d04855fab 100644 --- a/module/mail/model.php +++ b/module/mail/model.php @@ -66,7 +66,7 @@ class mailModel extends model $config->password = ''; $config->debug = 1; $config->charset = 'utf-8'; - if(!isset($config->secure)) $config->secure = ''; + if(!isset($config->secure)) $config->secure = 0; if(!isset($config->username)) $config->username = $username; if(!isset($config->host)) $config->host = ''; if(!isset($config->auth)) $config->auth = 1; @@ -92,7 +92,7 @@ class mailModel extends model $config->username = $username; $config->auth = 1; if(!isset($config->port)) $config->port = 25; - if(!isset($config->secure)) $config->secure = ''; + if(!isset($config->secure)) $config->secure = 0; return $config; } @@ -163,7 +163,7 @@ class mailModel extends model $config->host = $host; $config->auth = 1; $config->port = $port; - $config->secure = $port == 465 ? 'ssl' : ''; + $config->secure = $port == 465 ? 'ssl' : 0; return $config; } diff --git a/module/message/model.php b/module/message/model.php index 2f75ef7288..ee36200734 100755 --- a/module/message/model.php +++ b/module/message/model.php @@ -1,7 +1,7 @@ dataset)) { $dataSource = $this->dataset; include_once dirname(__FILE__) . '/dataset.php'; - $dataset = new dataset($dao); + $dataset = new dataset($dao, $config); $statement = $dataset->$dataSource(implode(',', $this->fieldList)); return $statement->queryString; @@ -303,13 +305,13 @@ class baseCalc * Print SQL of data source. * * @param object $dao + * @param object $config * @access public * @return mixed */ - public function printSQL($dao) + public function printSQL($dao, $config) { - a($dao); - echo $this->getSQL($dao); + echo $this->getSQL($dao, $config); } /** @@ -317,16 +319,17 @@ class baseCalc * Get rows of data source. * * @param object $dao + * @param object $config * @access public * @return array */ - public function getRows($dao = null) + public function getRows($dao, $config) { if(!empty($this->dataset)) { $dataSource = $this->dataset; include_once dirname(__FILE__) . '/dataset.php'; - $dataset = new dataset($dao); + $dataset = new dataset($dao, $config); $statement = $dataset->$dataSource(implode(',', $this->fieldList)); return $statement->fetchAll(); diff --git a/module/metric/dataset.php b/module/metric/dataset.php index 1cd9e1360b..f5a58bd7b8 100644 --- a/module/metric/dataset.php +++ b/module/metric/dataset.php @@ -532,12 +532,29 @@ class dataset */ public function getDevStories($fieldList) { - $caseQuery = $this->dao->select('story, COUNT(DISTINCT id) AS case_count')->from(TABLE_CASE)->groupBy('story')->get(); + if(strpos($fieldList, 't3.') === false) + { + $stmt = $this->dao->select($fieldList) + ->from(TABLE_STORY)->alias('t1') + ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') + ->where('t1.deleted')->eq('0') + ->andWhere('t2.deleted')->eq('0') + ->andWhere('t1.type')->eq('story') + ->andWhere('t1.isParent')->eq('0') + ->andWhere('t2.shadow')->eq('0'); + + return $this->defaultWhere($stmt, 't1'); + } + + $table = 'tmp_case_getDevStories'; + $this->dao->exec("DROP TABLE IF EXISTS `{$table}`"); + $this->dao->exec("CREATE TABLE `{$table}` AS SELECT `story`, COUNT(`id`) AS `case_count` FROM " . TABLE_CASE . 'GROUP BY `story`'); + $this->dao->exec("CREATE INDEX `story` ON `{$table}` (`story`)"); $stmt = $this->dao->select($fieldList) ->from(TABLE_STORY)->alias('t1') ->leftJoin(TABLE_PRODUCT)->alias('t2')->on('t1.product=t2.id') - ->leftJoin("($caseQuery)")->alias('t3')->on('t1.id=t3.story') + ->leftJoin("`{$table}`")->alias('t3')->on('t1.id=t3.story') ->where('t1.deleted')->eq('0') ->andWhere('t2.deleted')->eq('0') ->andWhere('t1.type')->eq('story') diff --git a/module/misc/lang/de.php b/module/misc/lang/de.php index 7dc2446548..751cfaaa16 100644 --- a/module/misc/lang/de.php +++ b/module/misc/lang/de.php @@ -114,7 +114,9 @@ $lang->misc->feature->promptExecImage = 'theme/default/images/main/prompt_exec $lang->misc->feature->promptLearnMore = 'https://www.zentao.net/book/zentaopms/1097.html'; /* Release Date. */ +$lang->misc->releaseDate['20.6'] = '2024-08-30'; $lang->misc->releaseDate['20.5'] = '2024-08-16'; +$lang->misc->releaseDate['18.13'] = '2024-08-09'; $lang->misc->releaseDate['20.4'] = '2024-08-02'; $lang->misc->releaseDate['20.3.0'] = '2024-07-22'; $lang->misc->releaseDate['20.2.0'] = '2024-07-10'; @@ -233,7 +235,9 @@ $lang->misc->releaseDate['7.1.stable'] = '2015-03-07'; $lang->misc->releaseDate['6.3.stable'] = '2014-11-07'; /* Release Detail. */ +$lang->misc->feature->all['20.6'][] = array('title' => "The workflow now allows for the configuration of multiple interfaces. The approval workflow can be customized to include conditional logic based on workflow fields. Bug fixes implemented.", 'desc' => ''); $lang->misc->feature->all['20.5'][] = array('title' => 'Document optimized; 23 new built-in metric items added.', 'desc' => ''); +$lang->misc->feature->all['18.13'][] = array('title' => "Enhanced list views for My Work, Story, Task, and Bug, along with improved detail pages for Product and Project. ZenTao now offers compatibility with Dameng database. Copying Stories, Tasks, Bugs, and Test Cases now includes the ability to copy custom fields and values added through workflow customization. Bug fixes implemented.", 'desc' => ''); $lang->misc->feature->all['20.4'][] = array('title' => 'Add a new message center. Enhance release management. Implement branch and tag management. Enable additional signers in the approval workflow.', 'desc' => ''); $lang->misc->feature->all['20.3.0'][] = array('title' => "The pivot table functionality now supports custom drill-down capabilities. The requirement pool feature allows users to view a matrix of requirements at multiple levels, including requirement pool requirements, business requirements, user requirements, and development requirements. When changes occur in the upstream requirement pool, the downstream requirements will need to be manually confirmed. The product roadmap can now associate business requirements and user requirements at any level of the hierarchy. We've optimized the operation button logic for managing requirement pool requirements, business requirements, and user requirements. The search tag conditions for these different types of requirements have also been streamlined. Additionally, when restoring a previously deleted requirement, the system will automatically recalculate the stage of the upstream requirements.", 'desc' => ''); $lang->misc->feature->all['20.2.0'][] = array('title' => "This release included optimizations to the product matrix, new features to configure platform-level application settings, improvements to the approval process workflow, additions to the OR interface to capture more business requirements, the ability to track new roadmaps and the Charter projected phase for user stories, support for unlimited levels of business requirements split, new estimation capabilities for the development stage of business requirements, support for estimating delivery stage when distributing or decomposing OR requirements, the option to distribute OR requirements as business requirements, estimation for OR requirements and user requirements during upgrades, and a new TR4A review point added to the development stage.", 'desc' => ''); diff --git a/module/misc/lang/en.php b/module/misc/lang/en.php index 51993d649f..615aa7f21e 100644 --- a/module/misc/lang/en.php +++ b/module/misc/lang/en.php @@ -114,7 +114,9 @@ $lang->misc->feature->promptExecImage = 'theme/default/images/main/prompt_exec $lang->misc->feature->promptLearnMore = 'https://www.zentao.net/book/zentaopms/1097.html'; /* Release Date. */ +$lang->misc->releaseDate['20.6'] = '2024-08-30'; $lang->misc->releaseDate['20.5'] = '2024-08-16'; +$lang->misc->releaseDate['18.13'] = '2024-08-09'; $lang->misc->releaseDate['20.4'] = '2024-08-02'; $lang->misc->releaseDate['20.3.0'] = '2024-07-22'; $lang->misc->releaseDate['20.2.0'] = '2024-07-10'; @@ -233,7 +235,9 @@ $lang->misc->releaseDate['7.1.stable'] = '2015-03-07'; $lang->misc->releaseDate['6.3.stable'] = '2014-11-07'; /* Release Detail. */ +$lang->misc->feature->all['20.6'][] = array('title' => "The workflow now allows for the configuration of multiple interfaces. The approval workflow can be customized to include conditional logic based on workflow fields. Bug fixes implemented.", 'desc' => ''); $lang->misc->feature->all['20.5'][] = array('title' => 'Document optimized; 23 new built-in metric items added.', 'desc' => ''); +$lang->misc->feature->all['18.13'][] = array('title' => "Enhanced list views for My Work, Story, Task, and Bug, along with improved detail pages for Product and Project. ZenTao now offers compatibility with Dameng database. Copying Stories, Tasks, Bugs, and Test Cases now includes the ability to copy custom fields and values added through workflow customization. Bug fixes implemented.", 'desc' => ''); $lang->misc->feature->all['20.4'][] = array('title' => 'Add a new message center. Enhance release management. Implement branch and tag management. Enable additional signers in the approval workflow.', 'desc' => ''); $lang->misc->feature->all['20.3.0'][] = array('title' => "The pivot table functionality now supports custom drill-down capabilities. The requirement pool feature allows users to view a matrix of requirements at multiple levels, including requirement pool requirements, business requirements, user requirements, and development requirements. When changes occur in the upstream requirement pool, the downstream requirements will need to be manually confirmed. The product roadmap can now associate business requirements and user requirements at any level of the hierarchy. We've optimized the operation button logic for managing requirement pool requirements, business requirements, and user requirements. The search tag conditions for these different types of requirements have also been streamlined. Additionally, when restoring a previously deleted requirement, the system will automatically recalculate the stage of the upstream requirements.", 'desc' => ''); $lang->misc->feature->all['20.2.0'][] = array('title' => "This release included optimizations to the product matrix, new features to configure platform-level application settings, improvements to the approval process workflow, additions to the OR interface to capture more business requirements, the ability to track new roadmaps and the Charter projected phase for user stories, support for unlimited levels of business requirements split, new estimation capabilities for the development stage of business requirements, support for estimating delivery stage when distributing or decomposing OR requirements, the option to distribute OR requirements as business requirements, estimation for OR requirements and user requirements during upgrades, and a new TR4A review point added to the development stage.", 'desc' => ''); diff --git a/module/misc/lang/fr.php b/module/misc/lang/fr.php index 3af5cd6a59..3a45c32117 100644 --- a/module/misc/lang/fr.php +++ b/module/misc/lang/fr.php @@ -114,7 +114,9 @@ $lang->misc->feature->promptExecImage = 'theme/default/images/main/prompt_exec $lang->misc->feature->promptLearnMore = 'https://www.zentao.net/book/zentaopms/1097.html'; /* Release Date. */ +$lang->misc->releaseDate['20.6'] = '2024-08-30'; $lang->misc->releaseDate['20.5'] = '2024-08-16'; +$lang->misc->releaseDate['18.13'] = '2024-08-09'; $lang->misc->releaseDate['20.4'] = '2024-08-02'; $lang->misc->releaseDate['20.3.0'] = '2024-07-22'; $lang->misc->releaseDate['20.2.0'] = '2024-07-10'; @@ -233,7 +235,9 @@ $lang->misc->releaseDate['7.1.stable'] = '2015-03-07'; $lang->misc->releaseDate['6.3.stable'] = '2014-11-07'; /* Release Detail. */ +$lang->misc->feature->all['20.6'][] = array('title' => "The workflow now allows for the configuration of multiple interfaces. The approval workflow can be customized to include conditional logic based on workflow fields. Bug fixes implemented.", 'desc' => ''); $lang->misc->feature->all['20.5'][] = array('title' => 'Document optimized; 23 new built-in metric items added.', 'desc' => ''); +$lang->misc->feature->all['18.13'][] = array('title' => "Enhanced list views for My Work, Story, Task, and Bug, along with improved detail pages for Product and Project. ZenTao now offers compatibility with Dameng database. Copying Stories, Tasks, Bugs, and Test Cases now includes the ability to copy custom fields and values added through workflow customization. Bug fixes implemented.", 'desc' => ''); $lang->misc->feature->all['20.4'][] = array('title' => 'Add a new message center. Enhance release management. Implement branch and tag management. Enable additional signers in the approval workflow.', 'desc' => ''); $lang->misc->feature->all['20.3.0'][] = array('title' => "The pivot table functionality now supports custom drill-down capabilities. The requirement pool feature allows users to view a matrix of requirements at multiple levels, including requirement pool requirements, business requirements, user requirements, and development requirements. When changes occur in the upstream requirement pool, the downstream requirements will need to be manually confirmed. The product roadmap can now associate business requirements and user requirements at any level of the hierarchy. We've optimized the operation button logic for managing requirement pool requirements, business requirements, and user requirements. The search tag conditions for these different types of requirements have also been streamlined. Additionally, when restoring a previously deleted requirement, the system will automatically recalculate the stage of the upstream requirements.", 'desc' => ''); $lang->misc->feature->all['20.2.0'][] = array('title' => "This release included optimizations to the product matrix, new features to configure platform-level application settings, improvements to the approval process workflow, additions to the OR interface to capture more business requirements, the ability to track new roadmaps and the Charter projected phase for user stories, support for unlimited levels of business requirements split, new estimation capabilities for the development stage of business requirements, support for estimating delivery stage when distributing or decomposing OR requirements, the option to distribute OR requirements as business requirements, estimation for OR requirements and user requirements during upgrades, and a new TR4A review point added to the development stage.", 'desc' => ''); diff --git a/module/misc/lang/zh-cn.php b/module/misc/lang/zh-cn.php index 3c76cafdbf..10b84e8ebd 100644 --- a/module/misc/lang/zh-cn.php +++ b/module/misc/lang/zh-cn.php @@ -114,7 +114,9 @@ $lang->misc->feature->promptExecImage = 'theme/default/images/main/prompt_exec $lang->misc->feature->promptLearnMore = 'https://www.zentao.net/book/zentaopms/1097.html'; /* Release Date. */ +$lang->misc->releaseDate['20.6'] = '2024-08-30'; $lang->misc->releaseDate['20.5'] = '2024-08-16'; +$lang->misc->releaseDate['18.13'] = '2024-08-09'; $lang->misc->releaseDate['20.4'] = '2024-08-02'; $lang->misc->releaseDate['20.3.0'] = '2024-07-22'; $lang->misc->releaseDate['20.2.0'] = '2024-07-10'; @@ -233,7 +235,9 @@ $lang->misc->releaseDate['7.1.stable'] = '2015-03-07'; $lang->misc->releaseDate['6.3.stable'] = '2014-11-07'; /* Release Detail. */ +$lang->misc->feature->all['20.6'][] = array('title' => '工作流支持配置多界面;审批流支持使用工作流中字段配置条件;修复Bug。', 'desc' => ''); $lang->misc->feature->all['20.5'][] = array('title' => '文档优化;新增23个内置度量项。', 'desc' => ''); +$lang->misc->feature->all['18.13'][] = array('title' => '性能优化:我的待办和需求、任务、Bug列表页面、项目、产品的详情页面。禅道兼容达梦数据库;复制需求、任务、Bug、用例时,可以复制工作流中添加的字段和值;修复Bug。', 'desc' => ''); $lang->misc->feature->all['20.4'][] = array('title' => '新增消息中心;完善发布管理;增加分支和标签管理;审批流支持加签设置。', 'desc' => ''); $lang->misc->feature->all['20.3.0'][] = array('title' => '透视表自定义下钻;需求池需求矩阵支持查看多层级的需求池需求、业务需求、用户需求和研发需求;上游需求池需求发生变更时,下级需求将需要进行确认;产品路标支持关联任意级的业务需求和用户需求;优化了需求池需求、业务需求和用户需求的操作按钮逻辑;优化了需求池需求、业务需求和用户需求的检索标签条件;还原已删除需求时重算上游需求的阶段。', 'desc' => ''); $lang->misc->feature->all['20.2.0'][] = array('title' => '产品矩阵的优化,增加平台版应用配置功能,优化审批流,OR界面新增业务需求,用需新增已设路标和Charter立项阶段,支持业用需求的无限层级,支持业用需求研发阶段的推算,支持分发和拆分OR需求时交付阶段的推算,OR需求支持分发为业务需求,实现老版本升级时OR需求和用户需求的推算,开发阶段增加TR4A评审点。', 'desc' => ''); diff --git a/module/my/control.php b/module/my/control.php index 5f759a3cfd..af152af4ee 100755 --- a/module/my/control.php +++ b/module/my/control.php @@ -1093,8 +1093,20 @@ class my extends control $this->app->loadClass('pager', true); $pager = pager::init($recTotal, $recPerPage, $pageID); - $feedbacks = $browseType != 'bysearch' ?$this->loadModel('feedback')->getList($browseType, $orderBy, $pager) : $this->loadModel('feedback')->getBySearch($queryID, $orderBy, $pager); - $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'workFeedback'); + $this->loadModel('feedback'); + if($browseType == 'assignedby') + { + $feedbacks = $this->my->getAssignedByMe($this->app->user->account, $pager, $orderBy, 'feedback'); + } + elseif($browseType == 'bysearch') + { + $feedbacks = $this->my->getFeedbacksBySearch($queryID, $orderBy, $pager); + } + else + { + $feedbacks = $this->feedback->getList($browseType, $orderBy, $pager); + } + $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'feedback', false); $this->loadModel('datatable'); $this->lang->datatable->moduleSetting = str_replace($this->lang->module, $this->lang->feedback->moduleAB, $this->lang->datatable->moduleSetting); @@ -1150,8 +1162,9 @@ class my extends control { $tickets = $this->loadModel('ticket')->getBySearch($queryID, $orderBy, $pager); } + foreach($tickets as $ticket) $ticket->feedbackTip = $ticket->feedback != 0 ? '#' . $ticket->feedback : ''; - $actionURL = $this->createLink('my', 'work', "mode=ticket&type=bysearch¶m=myQueryID&orderBy={$orderBy}&recTotal={$recTotal}&recPerPage={$recPerPage}&pageID={$pageID}"); + $actionURL = $this->createLink('my', $this->app->rawMethod, "mode=ticket&type=bysearch¶m=myQueryID&orderBy={$orderBy}&recTotal={$recTotal}&recPerPage={$recPerPage}&pageID={$pageID}"); $this->my->buildTicketSearchForm($queryID, $actionURL); $this->myZen->showWorkCount($recTotal, $recPerPage, $pageID); @@ -1405,7 +1418,14 @@ class my extends control if($_POST) { - foreach($_POST as $key => $value) $this->setting->setItem("{$this->app->user->account}.common.$key", $value); + $keyList = array('URSR', 'programLink', 'productLink', 'projectLink', 'executionLink'); + foreach($_POST as $key => $value) + { + if(!in_array($key, $keyList)) continue; + if($key != 'URSR' && !isset($this->lang->my->{$key . 'List'}[$value])) continue; + if($key == 'URSR') $value = (int)$value; + $this->setting->setItem("{$this->app->user->account}.common.$key", $value); + } $this->setting->setItem("{$this->app->user->account}.common.preferenceSetted", 1); diff --git a/module/my/js/audit.ui.js b/module/my/js/audit.ui.js index 90b3b950aa..6241be6134 100644 --- a/module/my/js/audit.ui.js +++ b/module/my/js/audit.ui.js @@ -21,7 +21,7 @@ window.onRenderCell = function(result, {row, col}) } else { - if(result[0].length) delete result[0].props.items[0]; + result[0].props.items[0]['disabled'] = true; } } if(result && col.name == 'title') diff --git a/module/my/model.php b/module/my/model.php index 3f71794c3a..7ebc27f8cb 100644 --- a/module/my/model.php +++ b/module/my/model.php @@ -219,7 +219,7 @@ class myModel extends model $contribute->myBugTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_BUG)->where('assignedTo')->eq($account)->andWhere('deleted')->eq(0)->fetch('count'); $contribute->docCreatedTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_DOC)->where('addedBy')->eq($account)->andWhere('deleted')->eq('0')->fetch('count'); $contribute->ownerProductTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_PRODUCT)->where('PO')->eq($account)->andWhere('deleted')->eq('0')->fetch('count'); - $contribute->involvedProjectTotal = (int)$this->dao->select('COUNT(1) AS count')->from(TABLE_PROJECT) + $contribute->involvedProjectTotal = (int)$this->dao->select('COUNT(DISTINCT id) AS count')->from(TABLE_PROJECT) ->where('deleted')->eq('0') ->andWhere('type')->eq('project') ->andWhere('id', true)->in($inTeam) @@ -285,6 +285,18 @@ class myModel extends model ->page($pager) ->fetchAll('id'); } + if($objectType == 'feedback' || $objectType == 'ticket') + { + return $this->dao->select('t1.*,t2.dept')->from($this->config->objectTables[$module])->alias('t1') + ->leftJoin(TABLE_USER)->alias('t2')->on('t1.openedBy = t2.account') + ->leftJoin(TABLE_PRODUCT)->alias('t3')->on('t1.product = t3.id') + ->where('t1.deleted')->eq(0) + ->andWhere('t3.deleted')->eq(0) + ->andWhere('t1.id')->in($objectIdList) + ->orderBy('t1.' . $orderBy) + ->page($pager) + ->fetchAll('id'); + } return $this->dao->select('*')->from($this->config->objectTables[$module]) ->where('deleted')->eq(0) ->andWhere('id')->in($objectIdList) @@ -803,7 +815,8 @@ class myModel extends model $grantProducts = $this->loadModel('feedback')->getGrantProducts(); - $this->config->ticket->search['module'] = 'workTicket'; + $queryName = $this->app->rawMethod . 'Ticket'; + $this->config->ticket->search['module'] = $queryName; $this->config->ticket->search['queryID'] = $queryID; $this->config->ticket->search['actionURL'] = $actionURL; $this->config->ticket->search['params']['product']['values'] = $grantProducts; diff --git a/module/my/ui/feedback.html.php b/module/my/ui/feedback.html.php index aed5cd5826..ccb7d3febb 100644 --- a/module/my/ui/feedback.html.php +++ b/module/my/ui/feedback.html.php @@ -71,7 +71,6 @@ dtable set::data(array_values($feedbacks)), set::checkable($canBatchAction), set::userMap($users), - set::fixedLeftWidth('44%'), set::orderBy($orderBy), set::customCols(true), set::sortLink(createLink('my', $app->rawMethod, "mode={$mode}&type={$type}¶m={$param}&orderBy={name}_{sortType}&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}&pageID={$pager->pageID}")), diff --git a/module/my/ui/testcase.html.php b/module/my/ui/testcase.html.php index af310a83ed..8316b97e4e 100644 --- a/module/my/ui/testcase.html.php +++ b/module/my/ui/testcase.html.php @@ -41,7 +41,7 @@ if($type == 'assigntome') $config->my->testcase->dtable->fieldList['actions']['list']['createBug']['url'] = array('module' => 'testcase', 'method' => 'createBug', 'params' => 'product={product}&caseID={case}&version={version}&runID={run}'); $config->my->testcase->dtable->fieldList['actions']['menu'] = array('runCase', 'runResult', 'edit', 'createBug', 'create'); } -foreach($config->my->testcase->dtable->fieldList['actions']['list'] as &$action) $action['url']['params'] = str_replace(array('{caseID}', '%executionID%'), array('{id}', '0'), $action['url']['params']); +foreach($config->my->testcase->dtable->fieldList['actions']['list'] as &$action) $action['url']['params'] = str_replace(array('{caseID}', '%executionID%', '{runID}'), array('{id}', '0', '0'), $action['url']['params']); $cases = initTableData($cases, $config->my->testcase->dtable->fieldList, $this->testcase); $data = array_values($cases); diff --git a/module/my/zen.php b/module/my/zen.php index babdfa5e21..44c36e6a77 100644 --- a/module/my/zen.php +++ b/module/my/zen.php @@ -130,23 +130,24 @@ class myZen extends my */ protected function buildSearchFormForFeedback(int $queryID, string $orderBy): void { - $products = $this->loadModel('product')->getPairs(); - - $this->config->feedback->search['module'] = 'workFeedback'; - $this->config->feedback->search['actionURL'] = inlink('work', "mode=feedback&browseType=bysearch¶m=myQueryID&orderBy={$orderBy}"); + $rawMethod = $this->app->rawMethod; + $this->config->feedback->search['module'] = $rawMethod . 'Feedback'; + $this->config->feedback->search['actionURL'] = inlink($rawMethod, "mode=feedback&browseType=bysearch¶m=myQueryID&orderBy={$orderBy}"); $this->config->feedback->search['queryID'] = $queryID; - $this->config->feedback->search['onMenuBar'] = 'no'; - $this->config->feedback->search['params']['product']['values'] = $products; + $this->config->feedback->search['params']['product']['values'] = $this->loadModel('product')->getPairs(); $this->config->feedback->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu(0, 'feedback', 0); $this->config->feedback->search['params']['processedBy']['values'] = $this->loadModel('feedback')->getFeedbackPairs('admin'); - unset($this->config->feedback->search['fields']['assignedTo']); - unset($this->config->feedback->search['fields']['closedBy']); - unset($this->config->feedback->search['fields']['closedDate']); - unset($this->config->feedback->search['fields']['closedReason']); - unset($this->config->feedback->search['fields']['processedBy']); - unset($this->config->feedback->search['fields']['processedDate']); - unset($this->config->feedback->search['fields']['solution']); + if($rawMethod == 'work') + { + unset($this->config->feedback->search['fields']['assignedTo']); + unset($this->config->feedback->search['fields']['closedBy']); + unset($this->config->feedback->search['fields']['closedDate']); + unset($this->config->feedback->search['fields']['closedReason']); + unset($this->config->feedback->search['fields']['processedBy']); + unset($this->config->feedback->search['fields']['processedDate']); + unset($this->config->feedback->search['fields']['solution']); + } $this->loadModel('search')->setSearchParams($this->config->feedback->search); } diff --git a/module/product/control.php b/module/product/control.php index 3fae95cf2d..3b5a2a9da8 100755 --- a/module/product/control.php +++ b/module/product/control.php @@ -234,7 +234,7 @@ class product extends control { if(!empty($_POST)) { - $productData = $this->productZen->buildProductForEdit(); + $productData = $this->productZen->buildProductForEdit($productID); $this->product->update($productID, $productData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -309,7 +309,7 @@ class product extends control { if(!empty($_POST)) { - $productData = $this->productZen->buildProductForActivate(); + $productData = $this->productZen->buildProductForActivate($productID); $this->product->activate($productID, $productData, $this->post->comment); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -342,7 +342,7 @@ class product extends control { if(!empty($_POST)) { - $productData = $this->productZen->buildProductForClose(); + $productData = $this->productZen->buildProductForClose($productID); $this->product->close($productID, $productData, $this->post->comment); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -1051,18 +1051,14 @@ class product extends control * * @param int $productID * @param string $branch - * @param int $planID - * @param string $fieldID - * @param int $needCreate - * @param string $expired - * @param string $param + * @param string $params + * @param bool $skipParent * @access public * @return void */ - public function ajaxGetPlans(int $productID, string $branch = '', int $planID = 0, string $fieldID = '', int $needCreate = 0, string $expired = '', string $param = '') + public function ajaxGetPlans(int $productID, string $branch = '', string $params = '', bool $skipParent = false) { - $param = strtolower($param); - $plans = $this->loadModel('productplan')->getPairs($productID, empty($branch) ? 'all' : $branch, $expired, strpos($param, 'skipparent') !== false); + $plans = $this->loadModel('productplan')->getPairs($productID, empty($branch) ? 'all' : $branch, $params, $skipParent); $items = array(); foreach($plans as $id => $name) diff --git a/module/product/tao.php b/module/product/tao.php index e2292c9752..f8f1b33fb9 100644 --- a/module/product/tao.php +++ b/module/product/tao.php @@ -62,7 +62,7 @@ class productTao extends productModel ->beginIF($programID)->andWhere('t1.program')->eq($programID)->fi() ->beginIF(strpos($mode, 'noclosed') !== false)->andWhere('t1.status')->ne('closed')->fi() ->beginIF(strpos("|$mode|", '|closed|') !== false)->andWhere('t1.status')->eq('closed')->fi() - ->beginIF(!$this->app->user->admin and $this->config->vision != 'lite')->andWhere('t1.id')->in($this->app->user->view->products)->fi() + ->beginIF(!$this->app->user->admin && $this->config->vision != 'lite' && $this->app->rawModule != 'repo')->andWhere('t1.id')->in($this->app->user->view->products)->fi() ->markRight(1) ->beginIF($append)->orWhere('(t1.id')->in($append)->markRight(1)->fi() ->markRight(1) diff --git a/module/product/test/lib/changestatus.ui.class.php b/module/product/test/lib/changestatus.ui.class.php new file mode 100644 index 0000000000..0a2be81115 --- /dev/null +++ b/module/product/test/lib/changestatus.ui.class.php @@ -0,0 +1,71 @@ +initForm('product', 'view', $productID, 'appIframe-product'); + $form->dom->btn($this->lang->product->close)->click();//产品详情页中点击关闭 + $form->wait(2); + $form->dom->confirmBtn->click();//弹窗中点击关闭产品 + $viewPage = $this->initForm('product', 'view', $productID, 'appIframe-product'); + $form->wait(2); + $closed = $this->lang->product->statusList->closed;//产品详情页中关闭状态的语言项 + //判断详情页中的状态是否为[关闭] + if($viewPage->dom->status->getText() == $closed) + { + return $this->success('关闭产品成功'); + } + return $this->failed('关闭产品失败'); + } + /** + * 激活产品 + * activate product + * + * @param $producrID 产品ID + * @return mixed + */ + public function activateProduct($productID) + { + $form = $this->initForm('product', 'view', $productID, 'appIframe-product'); + $form->dom->btn($this->lang->product->activate)->click();//产品详情页中点击激活 + $form->wait(2); + $form->dom->confirmBtn->click();//弹窗中点击激活产品 + $viewPage = $this->initForm('product', 'view', $productID, 'appIframe-product'); + $form->wait(2); + $normal = $this->lang->product->statusList->normal;//产品详情页中正常状态的语言项 + //判断详情页中的状态是否为[激活] + if($viewPage->dom->status->getText() == $normal) + { + return $this->success('激活产品成功'); + } + return $this->failed('激活产品失败'); + } + /** + * 删除产品 + * delete product + * + * @param $producrID 产品ID + * @return mixed + */ + public function deleteProduct($productID) + { + $form = $this->initForm('product', 'view', $productID, 'appIframe-product'); + $form->dom->delBtn->click();//产品详情页中点击删除 + $form->wait(1); + $form->dom->delConfirmBtn->click();//删除弹窗中点确认 + $form->wait(2); + $viewPage = $this->initForm('product', 'view', $productID, 'appIframe-product'); + $deleted = $this->lang->product->deleted;//产品详情页中已删除状态的语言项 + //判断详情页中的状态是否为[已删除] + return ($viewPage->dom->status->getText() == $deleted) ? $this->success('删除产品成功') : $this->failed('删除产品失败'); + } +} diff --git a/module/product/test/lib/editproduct.ui.class.php b/module/product/test/lib/editproduct.ui.class.php new file mode 100644 index 0000000000..02121740e0 --- /dev/null +++ b/module/product/test/lib/editproduct.ui.class.php @@ -0,0 +1,28 @@ +initForm('product', 'view', $productID, 'appIframe-product'); + $form->dom->editBtn->click(); + if(isset($product->name)) $form->dom->name->setValue($product->name); + $form->dom->btn($this->lang->save)->click(); + $form->wait(1); + if($this->response('method') != 'view') + { + if($this->checkFormTips('product')) return $this->success('编辑产品表单提示信息正确'); + return $this->failed('编辑产品表单提示信息不正确'); + } + $viewPage = $this->initForm('product', 'view', $productID, 'appIframe-product'); + if($viewPage->dom->productName->getText() != $product->name) return $this->failed('名称错误'); + return $this->success('产品编辑成功'); + } +} diff --git a/module/product/test/ui/changestatus.php b/module/product/test/ui/changestatus.php new file mode 100644 index 0000000000..4cb303c619 --- /dev/null +++ b/module/product/test/ui/changestatus.php @@ -0,0 +1,21 @@ +#!/usr/bin/env php +id->range('1'); +$product->name->range('产品1'); +$product->status->range('normal'); +$product->type->range('normal'); +$product->gen(1); + +$tester = new changeStatus(); +$tester->login(); + +$productID['productID'] = 1; +r($tester->closeProduct($productID)) && p('message,status') && e('关闭产品成功,SUCCESS'); +r($tester->activateProduct($productID)) && p('message,status') && e('激活产品成功,SUCCESS'); +r($tester->deleteProduct($productID)) && p('message,status') && e('删除产品成功,SUCCESS'); + +$tester->closeBrowser(); diff --git a/module/product/test/ui/editproduct.php b/module/product/test/ui/editproduct.php new file mode 100644 index 0000000000..84ec6f0072 --- /dev/null +++ b/module/product/test/ui/editproduct.php @@ -0,0 +1,24 @@ +#!/usr/bin/env php +id->range('1'); +$product->name->range('产品1'); +$product->status->range('normal'); +$product->type->range('normal'); +$product->gen(1); + +$tester = new editProductTester(); +$tester->login(); + +$productID['productID'] = 1; +$product = new stdClass(); +$product->name = ''; +r($tester->editProduct($productID, $product)) && p('message,status') && e('编辑产品必填提示信息正确,SUCCESS'); // 产品名称必填校验 + +$product->name = '产品1_编辑'; +r($tester->editProduct($productID, $product)) && p('message,status') && e('产品编辑成功,SUCCESS'); // 编辑产品 + +$tester->closeBrowser(); diff --git a/module/product/test/ui/page/view.php b/module/product/test/ui/page/view.php index 8957b4ccd5..0c5c623bbf 100644 --- a/module/product/test/ui/page/view.php +++ b/module/product/test/ui/page/view.php @@ -5,10 +5,15 @@ class viewPage extends page { parent::__construct($webdriver); $xpath = array( - 'productName' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[1]/div", - 'type' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[1]/span[2]", - 'acl' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[1]/span[3]", + 'productName' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[1]/div", + 'type' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[1]/span[2]", + 'acl' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[1]/span[3]", 'branchProductACL' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[1]/span[4]", + 'confirmBtn' => "/html/body/div[2]/div/div/div[3]/div[1]/div/form/div[2]/div/button", + 'status' => "//*[@id='mainContent']/div[1]/div[1]/div[1]/div[2]/div[1]/span[2]", + 'editBtn' => "//*[@id='mainContent']/div[2]/div/a[last()-1]", + 'delBtn' => "//*[@id='mainContent']/div[2]/div/a[last()]", + 'delConfirmBtn' => "//button[@z-key='confirm']", ); $this->dom->xpath = array_merge($this->dom->xpath, $xpath); } diff --git a/module/product/ui/browse.html.php b/module/product/ui/browse.html.php index d0d4953fae..aea5158862 100644 --- a/module/product/ui/browse.html.php +++ b/module/product/ui/browse.html.php @@ -66,9 +66,10 @@ $fnBuildCreateStoryButton = function() use ($lang, $product, $isProjectStory, $s { if(!common::canModify('product', $product)) return null; - global $app,$config; - $createLink = createLink($storyType, 'create', "product=" . (empty($productID) ? current(array_keys($projectProducts)) : $productID) . "&branch=$branch&moduleID=$moduleID&storyID=0&projectID=$projectID&bugID=0&planID=0&todoID=0&extra=&storyType=$storyType") . ($isProjectStory ? '#app=project' : ''); - $batchCreateLink = createLink($storyType, 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID&storyID=0&project=$projectID&plan=0&storyType=$storyType"). ($isProjectStory ? '#app=project' : ''); + global $app, $config; + $currentProductID = empty($productID) ? current(array_keys($projectProducts)) : $productID; + $createLink = createLink($storyType, 'create', "product=" . $currentProductID . "&branch=$branch&moduleID=$moduleID&storyID=0&projectID=$projectID&bugID=0&planID=0&todoID=0&extra=&storyType=$storyType") . ($isProjectStory ? '#app=project' : ''); + $batchCreateLink = createLink($storyType, 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID&storyID=0&project=$projectID&plan=0&storyType=$storyType"). ($isProjectStory ? '#app=project' : ''); $createBtnLink = ''; $createBtnTitle = ''; @@ -87,7 +88,7 @@ $fnBuildCreateStoryButton = function() use ($lang, $product, $isProjectStory, $s if(empty($createBtnLink)) return null; /* With batch create privileges then render the dropdown menu. */ - if(!empty($productID) && hasPriv($storyType, 'batchCreate') && hasPriv($storyType, 'create')) + if(hasPriv($storyType, 'batchCreate') && hasPriv($storyType, 'create')) { $items = array(); @@ -106,20 +107,20 @@ $fnBuildCreateStoryButton = function() use ($lang, $product, $isProjectStory, $s if($isProjectStory && $config->vision != 'lite') { - $batchItems[] = array('text' => $lang->SRCommon, 'url' => $batchCreateLink); + if(!empty($productID)) $batchItems[] = array('text' => $lang->SRCommon, 'url' => $batchCreateLink); if(str_contains($project->storyType, 'requirement') && $this->config->URAndSR) { - if(common::hasPriv('requirement', 'create')) $items[] = array('text' => $lang->requirement->create, 'url' => createLink('requirement', 'create', "product=$productID&branch=$branch&moduleID=$moduleID&requirementID=0&projectID=$projectID") . '#app=project'); - if(common::hasPriv('requirement', 'batchCreate')) $batchItems[] = array('text' => $lang->URCommon, 'url' => createLink('requirement', 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID&requirementID=0&project=$projectID") . '#app=project'); + if(common::hasPriv('requirement', 'create')) $items[] = array('text' => $lang->requirement->create, 'url' => createLink('requirement', 'create', "product=$currentProductID&branch=$branch&moduleID=$moduleID&requirementID=0&projectID=$projectID") . '#app=project'); + if(common::hasPriv('requirement', 'batchCreate') && !empty($productID)) $batchItems[] = array('text' => $lang->URCommon, 'url' => createLink('requirement', 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID&requirementID=0&project=$projectID") . '#app=project'); } if(str_contains($project->storyType, 'epic') && $this->config->enableER) { - if(common::hasPriv('epic', 'create')) $items[] = array('text' => $lang->epic->create, 'url' => createLink('epic', 'create', "product=$productID&branch=$branch&moduleID=$moduleID&epicID=0&projectID=$projectID") . '#app=project'); - if(common::hasPriv('epic', 'batchCreate')) $batchItems[] = array('text' => $lang->ERCommon, 'url' => createLink('epic', 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID&epicID=0&project=$projectID") . '#app=project'); + if(common::hasPriv('epic', 'create')) $items[] = array('text' => $lang->epic->create, 'url' => createLink('epic', 'create', "product=$currentProductID&branch=$branch&moduleID=$moduleID&epicID=0&projectID=$projectID") . '#app=project'); + if(common::hasPriv('epic', 'batchCreate') && !empty($productID)) $batchItems[] = array('text' => $lang->ERCommon, 'url' => createLink('epic', 'batchCreate', "productID=$productID&branch=$branch&moduleID=$moduleID&epicID=0&project=$projectID") . '#app=project'); } - $items[] = array('text' => $lang->story->batchCreate, 'items' => $batchItems); + if(!empty($productID)) $items[] = array('text' => $lang->story->batchCreate, 'items' => $batchItems); } else { @@ -128,6 +129,7 @@ $fnBuildCreateStoryButton = function() use ($lang, $product, $isProjectStory, $s return btnGroup ( + $app->tab == 'project' ? setData('app', 'project') : null, btn ( setClass(($app->tab != 'product' ? 'secondary' : 'primary') . ' create-story-btn'), @@ -255,7 +257,7 @@ $fnGenerateFootToolbar = function() use ($lang, $app, $product, $productID, $pro $canBatchChangeParent = $canBeChanged && hasPriv($storyType, 'batchChangeParent') && !($storyType == 'epic' && count($gradeGroup['epic']) < 2) && $app->tab == 'product'; $canBatchUnlink = $canBeChanged && $projectHasProduct && hasPriv('projectstory', 'batchUnlinkStory'); $canBatchImportToLib = $canBeChanged && $isProjectStory && in_array($this->config->edition, array('max', 'ipd')) && hasPriv('story', 'batchImportToLib') && helper::hasFeature('storylib'); - $canBatchChangeRoadmap = $canBeChanged && hasPriv('story', 'batchChangeRoadmap') && $config->vision == 'or' && $storyType == 'requirement'; + $canBatchChangeRoadmap = $canBeChanged && hasPriv($storyType, 'batchChangeRoadmap') && $config->vision == 'or' && $storyType == 'requirement'; $canBatchAction = $canBatchEdit || $canBatchClose || $canBatchReview || $canBatchChangeGrade || $canBatchChangeStage || $canBatchChangeModule || $canBatchChangePlan || $canBatchChangeParent || $canBatchAssignTo || $canBatchUnlink || $canBatchImportToLib || $canBatchChangeBranch || $canBatchChangeRoadmap; /* Remove empty data from data list. */ @@ -273,7 +275,7 @@ $fnGenerateFootToolbar = function() use ($lang, $app, $product, $productID, $pro foreach($branchTagOption as $branchID => $branchName) $branchItems[] = array('text' => $branchName, 'class' => 'batch-btn', 'data-formaction' => $this->createLink($isProjectStory ? 'projectstory' : $storyType, 'batchChangeBranch', "branchID=$branchID")); foreach($modules as $moduleID => $moduleName) $moduleItems[] = array('text' => $moduleName, 'class' => 'batch-btn', 'data-formaction' => $this->createLink($isProjectStory ? 'projectstory' : $storyType, 'batchChangeModule', "moduleID=$moduleID")); foreach($plans as $planID => $planName) $planItems[] = array('text' => $planName, 'class' => 'batch-btn', 'data-formaction' => $this->createLink($isProjectStory ? 'projectstory' : $storyType, 'batchChangePlan', "planID=$planID")); - foreach($noclosedRoadmaps as $roadmapID => $roadmapName) $roadmapItems[] = array('text' => empty($roadmapName) ? $lang->null : $roadmapName, 'class' => 'batch-btn', 'data-formaction' => $this->createLink('story', 'batchChangeRoadmap', "roadmapID=$roadmapID")); + foreach($noclosedRoadmaps as $roadmapID => $roadmapName) $roadmapItems[] = array('text' => empty($roadmapName) ? $lang->null : $roadmapName, 'class' => 'batch-btn', 'data-formaction' => $this->createLink($storyType, 'batchChangeRoadmap', "roadmapID=$roadmapID")); foreach($lang->story->stageList as $key => $stageName) { @@ -372,9 +374,13 @@ featureBar set::width('150px'), setStyle('justify-content', 'center'), set::display($lang->story->viewAllGrades), - set::menu(array('checkbox' => true)), + set::menu(array('checkbox' => true, 'itemProps' => array('innerComponent' => 'a'))), set::value($showGrades), - set::onPopHidden(jsRaw('setShowGrades')) + set::toolbar + ( + array('text' => $lang->confirm, 'onClick' => jsRaw('(e,info) => {setShowGrades();info.relativeTarget.close();}')), + array('text' => $lang->cancel, 'onClick' => jsRaw('(e,info) => info.relativeTarget.close()')), + ) ) ), set::current($storyBrowseType), diff --git a/module/product/zen.php b/module/product/zen.php index 3c068c3dfe..a85da2ce4e 100644 --- a/module/product/zen.php +++ b/module/product/zen.php @@ -567,13 +567,14 @@ class productZen extends product * 构建编辑产品的数据。 * Build product data for edit. * + * @param int $productID * @access protected * @return object */ - protected function buildProductForEdit(): object + protected function buildProductForEdit(int $productID): object { $editorFields = array_keys(array_filter(array_map(function($config){return (!empty($config['control']) && $config['control'] == 'editor');}, $this->config->product->form->edit))); - $productData = form::data($this->config->product->form->edit) + $productData = form::data($this->config->product->form->edit, $productID) ->setIF($this->post->acl == 'open', 'whitelist', '') ->get(); @@ -584,12 +585,13 @@ class productZen extends product * 构建激活产品数据。 * Build product data for activate. * + * @param int $productID * @access protected * @return object */ - protected function buildProductForActivate(): object + protected function buildProductForActivate(int $productID): object { - $productData = form::data($this->config->product->form->activate) + $productData = form::data($this->config->product->form->activate, $productID) ->setIF($this->config->vision == 'or', 'status', 'normal') ->get(); @@ -600,12 +602,13 @@ class productZen extends product * 构建关闭产品数据。 * Build product data for close. * + * @param int $productID * @access protected * @return object */ - protected function buildProductForClose(): object + protected function buildProductForClose(int $productID): object { - $productData = form::data($this->config->product->form->close)->get(); + $productData = form::data($this->config->product->form->close, $productID)->get(); return $this->loadModel('file')->processImgURL($productData, $this->config->product->editor->close['id'], $this->post->uid); } diff --git a/module/productplan/control.php b/module/productplan/control.php index c031fd4768..a4c5e92bad 100644 --- a/module/productplan/control.php +++ b/module/productplan/control.php @@ -130,7 +130,7 @@ class productplan extends control $plan = $this->productplan->getByID($planID); if(!empty($_POST)) { - $planData = form::data($this->config->productplan->form->edit) + $planData = form::data($this->config->productplan->form->edit, $planID) ->setIF($this->post->future || empty($_POST['begin']), 'begin', $this->config->productplan->future) ->setIF($this->post->future || empty($_POST['end']), 'end', $this->config->productplan->future) ->get(); @@ -151,7 +151,7 @@ class productplan extends control $oldBranch = array($planID => $plan->branch); /* Get the parent plan pair exclusion itself. */ - $parentPlanPairs = $this->productplan->getTopPlanPairs($plan->product, 'done,closed'); + $parentPlanPairs = $this->productplan->getTopPlanPairs($plan->product, 'done,closed', $plan->parent); unset($parentPlanPairs[$planID]); $this->view->parentPlanPairs = $parentPlanPairs; $this->view->parentList = $this->productplan->getByIDList(array_keys($this->view->parentPlanPairs)); @@ -555,7 +555,7 @@ class productplan extends control * @access public * @return void */ - public function linkStory(int $planID = 0, string $browseType = '', int $param = 0, string $orderBy = 'order_desc', int $recTotal = 0, int $recPerPage = 100, int $pageID = 1) + public function linkStory(int $planID = 0, string $browseType = '', int $param = 0, string $orderBy = 'id_desc', int $recTotal = 0, int $recPerPage = 100, int $pageID = 1) { if(!empty($_POST['stories'])) { @@ -582,11 +582,11 @@ class productplan extends control $planStories = $this->loadModel('story')->getPlanStories($planID); if($browseType == 'bySearch') { - $allStories = $this->story->getBySearch($plan->product, "0,{$plan->branch}", (int)$param, 'id_desc', 0, $this->config->enableER ? 'all' : 'story,requirement', array_keys($planStories), '', $pager); + $allStories = $this->story->getBySearch($plan->product, "0,{$plan->branch}", (int)$param, $orderBy, 0, $this->config->enableER ? 'all' : 'story,requirement', array_keys($planStories), '', $pager); } else { - $allStories = $this->story->getProductStories($this->view->product->id, $plan->branch ? "0,{$plan->branch}" : 0, '0', 'draft,reviewing,active,changing', $this->config->enableER ? 'all' : 'story,requirement', 'id_desc', true, array_keys($planStories), $pager); + $allStories = $this->story->getProductStories($this->view->product->id, $plan->branch ? "0,{$plan->branch}" : 0, '0', 'draft,reviewing,active,changing', $this->config->enableER ? 'all' : 'story,requirement', $orderBy, true, array_keys($planStories), $pager); } $modules = $this->loadModel('tree')->getOptionMenu($plan->product, 'story', 0, 'all'); diff --git a/module/productplan/model.php b/module/productplan/model.php index 2068a05336..2ce1144dd7 100644 --- a/module/productplan/model.php +++ b/module/productplan/model.php @@ -160,18 +160,21 @@ class productplanModel extends model * * @param int $productID * @param string $exclude + * @param int $append * @access public * @return array */ - public function getTopPlanPairs(int $productID, string $exclude = ''): array + public function getTopPlanPairs(int $productID, string $exclude = '', int $append = 0): array { - return $this->dao->select("id,title")->from(TABLE_PRODUCTPLAN) + $pairs = $this->dao->select("id,title")->from(TABLE_PRODUCTPLAN) ->where('product')->eq($productID) ->andWhere('parent')->le(0) ->andWhere('deleted')->eq(0) ->beginIF($exclude)->andWhere('status')->notin($exclude)->fi() ->orderBy('id_desc') ->fetchPairs(); + if($append) $pairs += $this->dao->select("id,title")->from(TABLE_PRODUCTPLAN)->where('id')->eq($append)->fetchPairs(); + return $pairs; } /** @@ -379,7 +382,6 @@ class productplanModel extends model ->where('product')->eq($productID) ->andWhere('deleted')->eq(0) ->beginIF(!empty($branchQuery))->andWhere($branchQuery)->fi() - ->beginIF($branches != '')->andWhere('branch')->in($branches)->fi() ->beginIF(strpos($param, 'unexpired') !== false)->andWhere('end')->ge(helper::today())->fi() ->orderBy('begin desc') ->fetchAll('id'); diff --git a/module/productplan/test/lib/batcheditplan.ui.class.php b/module/productplan/test/lib/batcheditplan.ui.class.php new file mode 100644 index 0000000000..22f4ddbac5 --- /dev/null +++ b/module/productplan/test/lib/batcheditplan.ui.class.php @@ -0,0 +1,65 @@ +initForm('productplan', 'browse', $planurl, 'appIframe-product'); + $form->wait(2); + $form->dom->selectAllBtn->click();//全选计划 + $form->dom->batchEditBtn->click();//点击编辑按钮 + $firstID = $form->dom->idIndex_static_0->getText();//获取第一个ID + $beginInputDom = "begin[{$firstID}]"; + $endInputDom = "end[{$firstID}]"; + //设置表单字段 + if (isset($productplan->title)) $form->dom->title_0->setValue($productplan->title); + if (isset($productplan->begin)) $form->dom->$beginInputDom->datePicker($productplan->begin); + if (isset($productplan->end)) $form->dom->$endInputDom->datePicker($productplan->end); + $form->dom->btn($this->lang->save)->click();//保存 + $form->wait(2); + return $this->checkBatchEdit($productplan, $form, $firstID); + } + /** + * 检查批量编辑计划的结果 + * Check the result of batch edit productplan + * + * @param object $productplan + * @param object $form + * @param string $firstID + * @return mixed + */ + public function checkBatchEdit($productplan, $form, $firstID) + { + //编辑失败时校验错误提示信息 + if ($this->response('method') != 'browse') + { + $titleTip = sprintf($this->lang->error->notempty, $this->lang->productplan->title); + $firstTitleTipDom = "title[{$firstID}]Tip";//第一行的名称提示信息 + $firstBeginTipDom = "begin[{$firstID}]Tip";//第一行的日期校验提示信息 + if ($form->dom->$firstBeginTipDom) + { + $beginTipform = $form->dom->$firstBeginTipDom->getText(); + $beginTip = sprintf($this->lang->productplan->beginGeEnd, $firstID); + return ($beginTipform == $beginTip) ? $this->success('日期校验提示信息正确') : $this->failed('日期校验提示信息不正确'); + } + $titleTipform = $form->dom->$firstTitleTipDom->getText(); + return ($titleTipform == $titleTip) ? $this->success('计划名称必填提示信息正确') : $this->failed('计划名称提示信息不正确'); + } + else + { + $viewUrl['planID'] = $firstID; + $viewPage = $this->initForm('productplan', 'view', $viewUrl, 'appIframe-product'); + $viewPage->waitElement($this->lang->productplan->view); + $viewPage->dom->btn($this->lang->productplan->view)->click();//进入计划详情页 + return ($viewPage->dom->planTitle->getText() == $productplan->title) ? $this->success('编辑计划成功') : $this->failed('编辑计划失败'); + } + } +} diff --git a/module/productplan/test/lib/changeplanstatus.ui.class.php b/module/productplan/test/lib/changeplanstatus.ui.class.php old mode 100755 new mode 100644 index f8b82e53df..92ce63eaf0 --- a/module/productplan/test/lib/changeplanstatus.ui.class.php +++ b/module/productplan/test/lib/changeplanstatus.ui.class.php @@ -22,21 +22,9 @@ class changePlanStatus extends tester //进入计划详情页 $form->dom->btn($this->lang->productplan->view)->click(); $form->wait(2); - //遍历statusList,找到[进行中]的语言项 - foreach($this->lang->productplan->statusList as $key => $status) - { - if ($key === 'doing') - { - $doing = $status; - break; - } - } + $doing = $this->lang->productplan->statusList->doing;//进行中的语言项 //判断详情页中的状态是否为[进行中] - if($viewPage->dom->status->getText() == $doing) - { - return $this->success('开始计划成功'); - } - return $this->failed('开始计划失败'); + return ($viewPage->dom->status->getText() == $doing) ? $this->success('开始计划成功') : $this->failed('开始计划失败'); } /** @@ -58,21 +46,9 @@ class changePlanStatus extends tester //进入计划详情页 $form->dom->btn($this->lang->productplan->view)->click(); $form->wait(2); - //遍历statusList,找到[已完成]的语言项 - foreach($this->lang->productplan->statusList as $key => $status) - { - if ($key === 'done') - { - $done = $status; - break; - } - } + $done = $this->lang->productplan->statusList->done;//已完成的语言项 //判断详情页中的状态是否为[已完成] - if($viewPage->dom->status->getText() == $done) - { - return $this->success('完成计划成功'); - } - return $this->failed('完成计划失败'); + return ($viewPage->dom->status->getText() == $done) ? $this->success('完成计划成功') : $this->failed('完成计划失败'); } /* @@ -94,21 +70,9 @@ class changePlanStatus extends tester //进入计划详情页 $form->dom->btn($this->lang->productplan->view)->click(); $form->wait(2); - //遍历statusList,找到[已关闭]的语言项 - foreach($this->lang->productplan->statusList as $key => $status) - { - if ($key === 'closed') - { - $closed = $status; - break; - } - } + $closed = $this->lang->productplan->statusList->closed;//已关闭的语言项 //判断详情页中的状态是否为[已关闭] - if($viewPage->dom->status->getText() == $closed) - { - return $this->success('关闭计划成功'); - } - return $this->failed('关闭计划失败'); + return ($viewPage->dom->status->getText() == $closed) ? $this->success('关闭计划成功') : $this->failed('关闭计划失败'); } /* @@ -130,20 +94,29 @@ class changePlanStatus extends tester //进入计划详情页 $form->dom->btn($this->lang->productplan->view)->click(); $form->wait(2); - //遍历statusList,找到[进行中]的语言项 - foreach($this->lang->productplan->statusList as $key => $status) - { - if ($key === 'doing') - { - $active = $status; - break; - } - } - //判断详情页中的状态是否为[已激活] - if($viewPage->dom->status->getText() == $active) - { - return $this->success('激活计划成功'); - } - return $this->failed('激活计划失败'); + $active = $this->lang->productplan->statusList->doing;//进行中的语言项 + //判断详情页中的状态是否为[进行中] + return ($viewPage->dom->status->getText() == $active) ? $this->success('激活计划成功') : $this->failed('激活计划失败'); + } + + /** + * 删除计划 + * delete plan + * + * @param $planID 计划ID + * @return mixed + */ + public function deletePlan($planID) + { + $form = $this->initForm('productplan', 'view', $planID, 'appIframe-product'); + $form->dom->btn($this->lang->delete)->click();//点击删除按钮 + $form->wait(1); + $form->dom->confirm->click();//弹窗中点确认 + $form->wait(1); + $viewPage = $this->initForm('productplan', 'view', $planID, 'appIframe-product'); + $form->wait(1); + $deleted = $this->lang->productplan->deleted;//已删除的语言项 + //判断详情页是否有[已删除]的标签 + return ($viewPage->dom->delTag->getText() == $deleted) ? $this->success('删除计划成功') : $this->failed('删除计划失败'); } } diff --git a/module/productplan/test/lib/createplan.ui.class.php b/module/productplan/test/lib/createplan.ui.class.php index ad062e789d..9b6907889c 100755 --- a/module/productplan/test/lib/createplan.ui.class.php +++ b/module/productplan/test/lib/createplan.ui.class.php @@ -20,7 +20,7 @@ class createPlanTester extends tester if (isset($productplan->title)) $form->dom->title->setValue($productplan->title); if (isset($productplan->begin)) $form->dom->begin->datePicker($productplan->begin); if (isset($productplan->future)) $form->dom->future->click(); - if (isset($productplan->end)) $form->dom->end->setValue($productplan->end); + if (isset($productplan->end)) $form->dom->end->datePicker($productplan->end); $form->dom->btn($this->lang->save)->click();//保存 $form->wait(3); @@ -58,7 +58,7 @@ class createPlanTester extends tester $browsePage = $this->initForm('productplan', 'browse', $planurl, 'appIframe-product'); //计划创建成功后的检查 $browsePage->dom->search->click(); - $browsePage->dom->searchInput1->setValue($productplan->title); + $browsePage->dom->value1->setValue($productplan->title); $browsePage->dom->searchBtn->click(); $browsePage->wait(2); // 获取搜索结果的第一个计划ID diff --git a/module/productplan/test/ui/batcheditplan.php b/module/productplan/test/ui/batcheditplan.php new file mode 100755 index 0000000000..ea895ef23b --- /dev/null +++ b/module/productplan/test/ui/batcheditplan.php @@ -0,0 +1,26 @@ +#!/usr/bin/env php +loadYaml('product', false, 2)->gen(10); +zendata('productplan')->loadYaml('productplan', false, 2)->gen(10); +$tester = new batchEditPlanTester(); +$tester->login(); + +$planurl['productID'] = 2; +$planurl['branch'] = 0; + +$productplan = new stdClass(); +$productplan->title = ''; +r($tester->batchEditPlan($productplan, $planurl)) && p('message,status') && e('计划名称必填提示信息正确,SUCCESS'); // 计划名称必填校验 + +$productplan->title = '计划_编辑后'; +r($tester->batchEditPlan($productplan, $planurl)) && p('message,status') && e('编辑计划成功,SUCCESS'); //编辑计划 + +$productplan->begin = '2024-06-24'; +$productplan->end = '2024-05-30'; +r($tester->batchEditPlan($productplan, $planurl)) && p('message,status') && e('日期校验提示信息正确,SUCCESS'); //校验结束日期不小于开始日期 + +$tester->closeBrowser(); diff --git a/module/productplan/test/ui/changeplanstatus.php b/module/productplan/test/ui/changeplanstatus.php index eb91eceb69..9b8fa8701e 100755 --- a/module/productplan/test/ui/changeplanstatus.php +++ b/module/productplan/test/ui/changeplanstatus.php @@ -15,4 +15,5 @@ r($tester->startPlan($planID)) && p('message,status') && e('开始计划成功, r($tester->finishPlan($planID)) && p('message,status') && e('完成计划成功,SUCCESS'); r($tester->closePlan($planID)) && p('message,status') && e('关闭计划成功,SUCCESS'); r($tester->activePlan($planID)) && p('message,status') && e('激活计划成功,SUCCESS'); +r($tester->deletePlan($planID)) && p('message,status') && e('删除计划成功,SUCCESS'); $tester->closeBrowser(); diff --git a/module/productplan/test/ui/page/browse.php b/module/productplan/test/ui/page/browse.php index 125e1adbdf..bece556d08 100755 --- a/module/productplan/test/ui/page/browse.php +++ b/module/productplan/test/ui/page/browse.php @@ -11,8 +11,9 @@ class browsePage extends page 'firstBegin' => "//*[@id='table-productplan-browse']/div[2]/div[2]/div/div[2]/div", 'firstEnd' => "//*[@id='table-productplan-browse']/div[2]/div[2]/div/div[3]/div", 'search' => "//*[@id='featureBar']/menu/li[last()]", - 'searchBtn' => "//*[@id='mainContent']/div[1]/div/div[1]/div[2]/button[1]", - 'searchInput1' => "//*[@id='mainContent']/div[1]/div/div[1]/div[1]/div[1]/table/tbody/tr/td[4]/input", + 'searchBtn' => "//*[@id='mainContent']/div[1]/div/form/div[2]/button[1]", + 'selectAllBtn' => "//*[@id='table-productplan-browse']/div[1]/div[1]/div/div[1]/div[1]/div/label", + 'batchEditBtn' => "//*[@id='table-productplan-browse']/div[3]/nav[1]/button", ); $this->dom->xpath = array_merge($this->dom->xpath, $xpath); } diff --git a/module/productplan/test/ui/page/view.php b/module/productplan/test/ui/page/view.php index d5a8de0133..c1fd63ef6e 100755 --- a/module/productplan/test/ui/page/view.php +++ b/module/productplan/test/ui/page/view.php @@ -6,6 +6,7 @@ class viewPage extends page parent::__construct($webdriver); $xpath = array( 'planTitle' => "//*[@id='planInfo']/table/tbody/tr[1]/td", + 'delTag' =>"//*[@id='mainContent']/div[1]/div/span[2]", 'confirm' => "//button[@z-key='confirm']", 'status' => "//*[@id='planInfo']/table/tbody/tr[last()-1]/td", 'begin' => "//*[@id='planInfo']/table/tbody/tr[2]/td", diff --git a/module/productplan/ui/browsebylist.html.php b/module/productplan/ui/browsebylist.html.php index 8320bcfaaf..dd2abb141f 100644 --- a/module/productplan/ui/browsebylist.html.php +++ b/module/productplan/ui/browsebylist.html.php @@ -81,18 +81,27 @@ if($canBatchAction) ); } + if($canBatchChangeStatus) + { + $footToolbar['items'][] = array( + 'text' => $lang->close, + 'className' => 'btn batch-btn size-sm secondary', + 'data-url' => $this->createLink('productplan', 'batchChangeStatus', "status=closed&productID={$productID}") + ); + } + if($canBatchChangeStatus) { $items = array(); foreach($lang->productplan->statusList as $statusKey => $statusText) { + if($statusKey == 'closed') continue; $items[$statusKey] = array ( 'text' => $statusText, 'class' => 'batch-btn ajax-btn not-open-url', 'data-url' => createLink('productplan', 'batchChangeStatus', "status={$statusKey}&productID={$productID}") ); - if($statusKey == 'closed') $items[$statusKey]['data-page'] = 'batch'; } menu diff --git a/module/productplan/ui/linkstory.html.php b/module/productplan/ui/linkstory.html.php index dbdb721424..11d02b377d 100644 --- a/module/productplan/ui/linkstory.html.php +++ b/module/productplan/ui/linkstory.html.php @@ -19,12 +19,11 @@ $cols['title']['nestedToggle'] = true; $cols['title']['data-toggle'] = 'modal'; $cols['title']['data-size'] = 'lg'; $cols['plan']['name'] = 'planTitle'; +$cols['plan']['sortType'] = false; $cols['assignedTo']['type'] = 'user'; $cols['module']['type'] = 'text'; $cols['module']['map'] = $modules; -foreach($cols as $colKey => $colConfig) $cols[$colKey]['sort'] = true; - foreach($allStories as $story) $story->estimate = $story->estimate . $config->hourUnit; $config->product->search['fields']['title'] = $lang->productplan->storyTitle; @@ -43,6 +42,8 @@ dtable set::userMap($users), set::cols($cols), set::data(array_values($allStories)), + set::orderBy($orderBy), + set::sortLink(inlink('linkStory', "planID={$plan->id}&browseType=$browseType¶m=$param&orderBy={name}_{sortType}")), set::noNestedCheck(), set::onRenderCell(jsRaw('window.renderStoryCell')), set::extraHeight('+144'), diff --git a/module/productplan/ui/view.html.php b/module/productplan/ui/view.html.php index 291550d6f0..af1a6dbd97 100644 --- a/module/productplan/ui/view.html.php +++ b/module/productplan/ui/view.html.php @@ -235,6 +235,7 @@ detailBody ( btn ( + $app->tab == 'project' ? setData('app', 'project') : null, set::text($lang->story->create), setClass('open-url secondary' . (empty($createStoryLink) ? ' disabled' : '')), set::icon('plus'), @@ -242,12 +243,12 @@ detailBody set::url($createStoryLink) ), set::items(array( - $this->config->URAndSR ? array('text' => $lang->requirement->create, 'url' => $createRequirementLink, 'class' => empty($createRequirementLink) ? 'disabled' : '') : null, - $this->config->enableER ? array('text' => $lang->epic->create, 'url' => $createEpicLink, 'class' => empty($createEpicLink) ? 'disabled' : '') : null, + $this->config->URAndSR ? array('text' => $lang->requirement->create, 'url' => $createRequirementLink, 'class' => empty($createRequirementLink) ? 'disabled' : '', 'data-app' => $app->tab) : null, + $this->config->enableER ? array('text' => $lang->epic->create, 'url' => $createEpicLink, 'class' => empty($createEpicLink) ? 'disabled' : '', 'data-app' => $app->tab) : null, array('text' => $lang->story->batchCreate, 'items' => array( - array('text' => $lang->SRCommon, 'url' => $batchCreateStoryLink, 'class' => empty($batchCreateStoryLink) ? 'disabled' : ''), - $this->config->URAndSR ? array('text' => $lang->URCommon, 'url' => $batchCreateRequirementLink, 'class' => empty($batchCreateRequirementLink) ? 'disabled' : '') : null, - $this->config->enableER ? array('text' => $lang->ERCommon, 'url' => $batchCreateEpicLink, 'class' => empty($batchCreateEpicLink) ? 'disabled' : '') : null + array('text' => $lang->SRCommon, 'url' => $batchCreateStoryLink, 'class' => empty($batchCreateStoryLink) ? 'disabled' : '', 'data-app' => $app->tab), + $this->config->URAndSR ? array('text' => $lang->URCommon, 'url' => $batchCreateRequirementLink, 'class' => empty($batchCreateRequirementLink) ? 'disabled' : '', 'data-app' => $app->tab) : null, + $this->config->enableER ? array('text' => $lang->ERCommon, 'url' => $batchCreateEpicLink, 'class' => empty($batchCreateEpicLink) ? 'disabled' : '', 'data-app' => $app->tab) : null )) )), set::trigger('hover'), diff --git a/module/program/control.php b/module/program/control.php index ad8b78ca15..d90f5d9e2e 100644 --- a/module/program/control.php +++ b/module/program/control.php @@ -245,7 +245,7 @@ class program extends control $hasUnfinished = $this->program->hasUnfinishedChildren($program); if($hasUnfinished) return $this->send(array('result' => 'fail', 'callback' => "zui.Modal.alert('{$this->lang->program->closeErrorMessage}');")); - $programData = form::data()->get(); + $programData = form::data(null, $programID)->get(); $changes = $this->program->close($programData, $program); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -282,7 +282,7 @@ class program extends control if(!empty($_POST)) { - $postData = form::data($this->config->program->form->start); + $postData = form::data($this->config->program->form->start, $programID); $postData = $this->programZen->prepareStartExtras($postData); $changes = $this->project->start($programID, $postData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -320,7 +320,7 @@ class program extends control if(!empty($_POST)) { - $programData = form::data()->get(); + $programData = form::data(null, $programID)->get(); $changes = $this->program->activate($programData, $program); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -357,6 +357,7 @@ class program extends control { $this->loadModel('action'); + $this->extendRequireFields($programID); if(!empty($_POST)) { $postData = fixer::input('post') diff --git a/module/program/test/lib/productview.ui.class.php b/module/program/test/lib/productview.ui.class.php new file mode 100644 index 0000000000..a93c2b6f2a --- /dev/null +++ b/module/program/test/lib/productview.ui.class.php @@ -0,0 +1,71 @@ +openUrl('program', 'productview'); + $form = $this->loadPage('program', 'productview'); + $form->dom->caretBtn->click(); + $form->dom->addProduct->click(); + $form->wait(1); + + $form->dom->switchToIframe(''); + $form->dom->switchToIframe('appIframe-product'); + $form->dom->program->picker('项目集3'); + $form->dom->name->setValue('产品A'); + $form->dom->btn($this->lang->save)->click(); + $form->wait(1); + + /*检查产品是否显示在了产品视角下*/ + $this->openUrl('program', 'productview'); + $form = $this->loadPage('program', 'productview'); + $form->dom->fstProgram->click(); + $form->wait(1); + if($form->dom->fstProduct->getText() != '产品A') return $this->failed('产品视角下创建产品失败'); + $this->openUrl('program', 'productView'); + return $this->success('产品视角下创建产品成功'); + } + + /** + * 产品视角下维护产品线。 + * Manage product line in the product view. + * + * @param string $programName + * @access public + * @return void + */ + public function manageProductLine() + { + /*维护表单*/ + $this->openUrl('program', 'productview'); + $form = $this->loadPage('program', 'productview'); + $form->dom->caretBtn->click(); + $form->dom->manageProductLine->click(); + $form->wait(1); + + $form->dom->productLineName->setValue('产品线A'); + $form->dom->ownProgram->picker('项目集3'); + $form->wait(1); + $form->dom->btn($this->lang->save)->click(); + + /*检查产品线是否创建成功*/ + $this->openUrl('program', 'productview'); + $form = $this->loadPage('program', 'productView'); + $form->dom->search(array("项目集名称,=,项目集3")); + $form->wait(1); + if($form->dom->productLine->getText() != '产品线A') return $this->failed('维护产品线失败'); + $this->openUrl('program', 'productView'); + + return $this->success('维护产品线成功'); + } +} diff --git a/module/program/test/ui/page/productview.php b/module/program/test/ui/page/productview.php new file mode 100644 index 0000000000..89dd5d1cca --- /dev/null +++ b/module/program/test/ui/page/productview.php @@ -0,0 +1,20 @@ + '//*[@class="panel panel-form page-form is-lite"]/div[2]/div/form/div[1]/div[1]/div', + 'caretBtn' => '//*[@id="actionBar"]/div/button', + 'addProduct' => '//*[@class="popover show fade dropdown in"]/menu/menu/li[2]/a', + 'productLine' => '//*[@id="productviews"]/div[2]/div[1]/div[1]/div[3]/div[1]', + 'manageProductLine' => '//*[@class="popover show fade dropdown in"]/menu/menu/li[3]/a', + 'productLineName' => '//*[@id="zin_product_manageline_form"]/div[2]/div[1]/div[1]/input[1]', + 'ownProgram' => '//*[@id="zin_product_manageline_form"]/div[2]/div[1]/div[2]/div[1]/div[1]', + 'fstProgram' => '/html/body/div[1]/div/div[2]/div/div/div[2]/div[1]/div/div[1]/div/a[1]', + 'fstProduct' => '/html/body/div[1]/div/div[2]/div/div/div[2]/div[1]/div/div/div/a', + ); + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/program/test/ui/productview.php b/module/program/test/ui/productview.php new file mode 100644 index 0000000000..ae45b5a786 --- /dev/null +++ b/module/program/test/ui/productview.php @@ -0,0 +1,19 @@ +#!/usr/bin/env php +login(); + +r($tester->createProgramProduct()) && p('message,status') && e('产品视角下创建产品成功,SUCCESS'); //产品视角下创建产品成功 +r($tester->manageProductLine()) && p('message,status') && e('产品视角下维护产品线成功,SUCCESS'); //产品视角下创建产品成功 diff --git a/module/program/zen.php b/module/program/zen.php index 6a69a83487..d70ae5fcd1 100644 --- a/module/program/zen.php +++ b/module/program/zen.php @@ -82,7 +82,7 @@ class programZen extends program } $this->app->loadConfig('project'); - $program = form::data($fields) + $program = form::data($fields, $programID) ->setDefault('lastEditedBy', $this->app->user->account) ->setDefault('lastEditedDate', helper::now()) ->setIF(helper::isZeroDate($this->post->begin), 'begin', '') diff --git a/module/programplan/js/create.ui.js b/module/programplan/js/create.ui.js index c790fc88e3..5b45f14b51 100644 --- a/module/programplan/js/create.ui.js +++ b/module/programplan/js/create.ui.js @@ -2,6 +2,7 @@ window.onRenderRow = function(row, rowIdx, data) { if(row.children('[data-name=milestone]').find('input[type=radio]:checked').length == 0) row.children('[data-name=milestone]').find('input[type=radio]').eq(1).prop('checked', true); row.children('[data-name=type]').find('[name^=type]').picker({disabled: true}); + if(typeof data != 'undefined' && typeof data.id != 'undefined') row.find('[data-type="delete"]').css('display', 'none'); if(project.model == 'ipd' && planID == '0') { diff --git a/module/programplan/lang/de.php b/module/programplan/lang/de.php index d89920a9a9..51f5eb2843 100644 --- a/module/programplan/lang/de.php +++ b/module/programplan/lang/de.php @@ -22,6 +22,7 @@ $lang->programplan->activate = 'Activate Stage'; $lang->programplan->createSubPlan = 'Create Sub Plan'; $lang->programplan->subPlanManage = 'Sub-stages management'; $lang->programplan->submit = 'Submit'; +$lang->programplan->idAB = 'ID'; $lang->programplan->parent = 'Parent Stage'; $lang->programplan->emptyParent = 'N/A'; diff --git a/module/programplan/lang/en.php b/module/programplan/lang/en.php index 0b3bd1ff66..ee25e75353 100644 --- a/module/programplan/lang/en.php +++ b/module/programplan/lang/en.php @@ -22,6 +22,7 @@ $lang->programplan->activate = 'Activate Stage'; $lang->programplan->createSubPlan = 'Create Sub Stage'; $lang->programplan->subPlanManage = 'Sub-stages management'; $lang->programplan->submit = 'Submit'; +$lang->programplan->idAB = 'ID'; $lang->programplan->parent = 'Parent Stage'; $lang->programplan->emptyParent = 'N/A'; diff --git a/module/programplan/lang/fr.php b/module/programplan/lang/fr.php index d89920a9a9..51f5eb2843 100644 --- a/module/programplan/lang/fr.php +++ b/module/programplan/lang/fr.php @@ -22,6 +22,7 @@ $lang->programplan->activate = 'Activate Stage'; $lang->programplan->createSubPlan = 'Create Sub Plan'; $lang->programplan->subPlanManage = 'Sub-stages management'; $lang->programplan->submit = 'Submit'; +$lang->programplan->idAB = 'ID'; $lang->programplan->parent = 'Parent Stage'; $lang->programplan->emptyParent = 'N/A'; diff --git a/module/programplan/lang/zh-cn.php b/module/programplan/lang/zh-cn.php index 600211fe6c..e300495114 100644 --- a/module/programplan/lang/zh-cn.php +++ b/module/programplan/lang/zh-cn.php @@ -22,6 +22,7 @@ $lang->programplan->activate = '激活阶段'; $lang->programplan->createSubPlan = '创建子阶段'; $lang->programplan->subPlanManage = '子阶段的管理方法'; $lang->programplan->submit = '提交评审'; +$lang->programplan->idAB = '序号'; $lang->programplan->parent = '父阶段'; $lang->programplan->emptyParent = '无'; diff --git a/module/programplan/model.php b/module/programplan/model.php index cadf75c734..2e27dbd8a5 100755 --- a/module/programplan/model.php +++ b/module/programplan/model.php @@ -338,7 +338,7 @@ class programplanModel extends model if($plan->id) { $stageID = $plan->id; - unset($plan->id, $plan->type, $plan->order); + unset($plan->id, $plan->type); $changes = $this->programplanTao->updateRow($stageID, $projectID, $plan); if(dao::isError()) return false; if(empty($changes)) continue; diff --git a/module/programplan/tao.php b/module/programplan/tao.php index 140b11e5e6..fb407ffd9b 100644 --- a/module/programplan/tao.php +++ b/module/programplan/tao.php @@ -532,7 +532,7 @@ class programplanTao extends programplanModel $plan->stageBy = empty($project) ? 'product' : $project->stageBy; $plan->version = 1; $plan->parentVersion = $plan->parent == 0 ? 0 : $this->dao->findByID($plan->parent)->from(TABLE_PROJECT)->fetch('version'); - $plan->team = substr($plan->name,0, 30); + $plan->team = mb_substr($plan->name, 0, 30); $plan->openedBy = $account; $plan->openedDate = helper::now(); $plan->openedVersion = $this->config->version; diff --git a/module/programplan/test/lib/createstage.ui.class.php b/module/programplan/test/lib/createstage.ui.class.php new file mode 100755 index 0000000000..ad069e32e1 --- /dev/null +++ b/module/programplan/test/lib/createstage.ui.class.php @@ -0,0 +1,75 @@ +initForm('programplan', 'create' , array('projectID' => 5), 'appIframe-project'); + + //删除除需求外的其他默认阶段行 + for ($i = 6; $i > 1; $i--) + { + $btn = "deleteBtn_$i"; + $programplanForm->dom->$btn->click(); + $programplanForm->wait(1); + } + + if(isset($waterfall['name_0'])) $programplanForm->dom->name_0->setValue($waterfall['name_0']); + if(isset($waterfall['begin_0'])) $programplanForm->dom->begin->setValue($waterfall['begin_0']); + if(isset($waterfall['end_0'])) $programplanForm->dom->end->setValue($waterfall['end_0']); + $programplanForm->wait(1); + $programplanForm->dom->submitBtn->click(); + $programplanForm->wait(1); + + if($this->response('module') != 'project') + { + if($this->checkFormTips('programplan')) return $this->success('创建阶段表单页提示信息正确'); + if($programplanForm->dom->nameTip) + { + //检查阶段名称不能为空 + $nameTipText = $programplanForm->dom->nameTip->getText(); + $nameTip = sprintf($this->lang->error->notempty, $this->lang->programplan->name); + return ($nameTipText == $nameTip) ? $this->success('创建阶段表单页提示信息正确') : $this->failed('创建阶段表单页提示信息不正确'); + } + if($programplanForm->dom->beginTip) + { + //检查计划开始日期不能为空 + $beginTipText = $programplanForm->dom->beginTip->getText(); + $beginTip = sprintf($this->lang->error->notempty, $this->lang->programplan->begin); + return ($beginTipText == $beginTip) ? $this->success('创建阶段表单页提示信息正确') : $this->failed('创建阶段表单页提示信息不正确'); + } + if($programplanForm->dom->endTip) + { + //检查计划结束日期不能为空 + if($waterfall['end_0'] == '') + { + $endTipText = $programplanForm->dom->endTip->getText(); + $endTip = sprintf($this->lang->error->notempty, $this->lang->programplan->end); + return ($endTipText == $endTip) ? $this->success('创建阶段表单页提示信息正确') : $this->failed('创建阶段表单页提示信息不正确'); + } + //检查计划开始不能大于计划完成 + if($waterfall['begin_0'] > $waterfall['end_0']) + { + $endTipText = $programplanForm->dom->endTip->getText(); + $endTip = sprintf($this->lang->programplan->error->planFinishSmall, ''); + return ($endTipText == $endTip) ? $this->success('创建阶段表单页提示信息正确') : $this->failed('创建阶段表单页提示信息不正确'); + } + } + return $this->failed('创建阶段表单页提示信息不正确'); + } + + $executionPage = $this->loadPage('project', 'execution'); + if($executionPage->dom->name->getText() != $waterfall['name_0']) return $this->failed('阶段名称错误'); + if($executionPage->dom->begin->getText() != $waterfall['begin_0']) return $this->failed('阶段计划开始错误'); + if($executionPage->dom->end->getText() != $waterfall['end_0']) return $this->failed('阶段计划完成错误'); + + return $this->success(); + } +} diff --git a/module/programplan/test/lib/createsubstage.ui.class.php b/module/programplan/test/lib/createsubstage.ui.class.php new file mode 100755 index 0000000000..66e87f3a56 --- /dev/null +++ b/module/programplan/test/lib/createsubstage.ui.class.php @@ -0,0 +1,68 @@ +initForm('programplan', 'create' , array('projectID' => 60, 'productID' => 0, 'executionID' => 106), 'appIframe-project'); + + if(isset($waterfall['name_0'])) $programplanForm->dom->name_0->setValue($waterfall['name_0']); + if(isset($waterfall['begin_0'])) $programplanForm->dom->begin->setValue($waterfall['begin_0']); + if(isset($waterfall['end_0'])) $programplanForm->dom->end->setValue($waterfall['end_0']); + $programplanForm->wait(1); + $programplanForm->dom->submitBtn->click(); + $programplanForm->wait(1); + + if($this->response('module') != 'project') + { + if($this->checkFormTips('programplan')) return $this->success('创建子阶段表单页提示信息正确'); + if($programplanForm->dom->nameTip) + { + //检查阶段名称不能为空 + $nameTipText = $programplanForm->dom->nameTip->getText(); + $nameTip = sprintf($this->lang->error->notempty, $this->lang->programplan->name); + return ($nameTipText == $nameTip) ? $this->success('创建子阶段表单页提示信息正确') : $this->failed('创建子阶段表单页提示信息不正确'); + } + if($programplanForm->dom->beginTip) + { + //检查计划开始日期不能为空 + $beginTipText = $programplanForm->dom->beginTip->getText(); + $beginTip = sprintf($this->lang->error->notempty, $this->lang->programplan->begin); + return ($beginTipText == $beginTip) ? $this->success('创建子阶段表单页提示信息正确') : $this->failed('创建子阶段表单页提示信息不正确'); + } + if($programplanForm->dom->endTip) + { + //检查计划结束日期不能为空 + if($waterfall['end_0'] == '') + { + $endTipText = $programplanForm->dom->endTip->getText(); + $endTip = sprintf($this->lang->error->notempty, $this->lang->programplan->end); + return ($endTipText == $endTip) ? $this->success('创建子阶段表单页提示信息正确') : $this->failed('创建子阶段表单页提示信息不正确'); + } + //检查计划开始不能大于计划完成 + if($waterfall['begin_0'] > $waterfall['end_0']) + { + $endTipText = $programplanForm->dom->endTip->getText(); + $endTip = sprintf($this->lang->programplan->error->planFinishSmall, ''); + return ($endTipText == $endTip) ? $this->success('创建子阶段表单页提示信息正确') : $this->failed('创建子阶段表单页提示信息不正确'); + } + } + return $this->failed('创建子阶段表单页提示信息不正确'); + } + + $executionPage = $this->loadPage('project', 'execution'); + if($executionPage->dom->subName->getText() != $waterfall['name_0']) return $this->failed('阶段名称错误'); + if($executionPage->dom->subBegin->getText() != $waterfall['begin_0']) return $this->failed('阶段计划开始错误'); + if($executionPage->dom->subEnd->getText() != $waterfall['end_0']) return $this->failed('阶段计划完成错误'); + + return $this->success(); + } + +} diff --git a/module/programplan/test/lib/editstage.ui.class.php b/module/programplan/test/lib/editstage.ui.class.php new file mode 100755 index 0000000000..a999b4adf7 --- /dev/null +++ b/module/programplan/test/lib/editstage.ui.class.php @@ -0,0 +1,70 @@ +initForm('project', 'execution' , array('status' => 'undone', 'projectID' => 60), 'appIframe-project'); + $form->dom->editBtn->click(); + $editForm = $this->loadPage('programplan', 'edit'); + + if(isset($waterfall['name'])) $editForm->dom->name->setValue($waterfall['name']); + if(isset($waterfall['begin'])) $editForm->dom->begin->setValue($waterfall['begin']); + if(isset($waterfall['end'])) $editForm->dom->end->setValue($waterfall['end']); + + $editForm->wait(1); + $editForm->dom->submitBtn->click(); + $editForm->wait(1); + return $this->checkResult($editForm, $waterfall); + } + + /** + * Check the result after edit the stage. + * 编辑阶段后检查结果。 + * + * @param array $waterfall + * @access public + * @return object + */ + public function checkResult(object $editForm, array $waterfall) + { + if($this->response('module') == 'project') + { + if($editForm->dom->nameTip) + { + //检查阶段名称不能为空 + $nameTipText = $editForm->dom->nameTip->getText(); + $nameTip = sprintf($this->lang->error->notempty, $this->lang->programplan->name); + return ($nameTipText == $nameTip) ? $this->success('编辑阶段表单页提示信息正确') : $this->failed('编辑阶段表单页提示信息不正确'); + } + if($editForm->dom->beginTip) + { + //检查计划开始日期不能为空 + $beginTipText = $editForm->dom->beginTip->getText(); + $beginTip = sprintf($this->lang->error->notempty, $this->lang->programplan->begin); + return ($beginTipText == $beginTip) ? $this->success('编辑阶段表单页提示信息正确') : $this->failed('编辑阶段表单页提示信息不正确'); + } + if($editForm->dom->endTip) + { + //检查计划结束日期不能为空 + $endTipText = $editForm->dom->endTip->getText(); + $endTip = sprintf($this->lang->error->notempty, $this->lang->programplan->end); + return ($endTipText == $endTip) ? $this->success('编辑阶段表单页提示信息正确') : $this->failed('编辑阶段表单页提示信息不正确'); + } + } + + $executionPage = $this->loadPage('project', 'execution'); + if($executionPage->dom->name->getText() != $waterfall['name']) return $this->failed('阶段名称错误'); + if($executionPage->dom->planBegin->getText() != $waterfall['begin']) return $this->failed('阶段计划开始错误'); + if($executionPage->dom->planEnd->getText() != $waterfall['end']) return $this->failed('阶段计划完成错误'); + return $this->success(); + } +} diff --git a/module/programplan/test/ui/createstage.php b/module/programplan/test/ui/createstage.php new file mode 100755 index 0000000000..47653c7b08 --- /dev/null +++ b/module/programplan/test/ui/createstage.php @@ -0,0 +1,32 @@ +#!/usr/bin/env php +loadYaml('project', false, 2)->gen(10); +$tester = new createstageTester(); +$tester->login(); + +$waterfall = array( + array('name_0' => '', 'begin_0' => date('Y-m-d'), 'end_0' => date('Y-m-d', strtotime('+30 days'))), + array('begin_0' => '', 'end_0' => date('Y-m-d', strtotime('+30 days'))), + array('begin_0' => date('Y-m-d'), 'end_0' => ''), + array('begin_0' => '2024-08-02', 'end_0' => '2024-08-01'), + array('name_0' => '需求阶段', 'begin_0' => '2024-06-27', 'end_0' => '2024-06-30'), +); + +r($tester->createStage($waterfall['0'])) && p('message,status') && e('创建阶段表单页提示信息正确,success'); //校验阶段名称不能为空 +r($tester->createStage($waterfall['1'])) && p('message,status') && e('创建阶段表单页提示信息正确,success'); //校验计划开始必填 +r($tester->createStage($waterfall['2'])) && p('message,status') && e('创建阶段表单页提示信息正确,success'); //校验计划完成必填 +r($tester->createStage($waterfall['3'])) && p('message,status') && e('创建阶段表单页提示信息正确,success'); //校验计划完成必须大于计划开始 +r($tester->createStage($waterfall['4'])) && p('status') && e('success'); //创建需求阶段 + +$tester->closeBrowser(); diff --git a/module/programplan/test/ui/createsubstage.php b/module/programplan/test/ui/createsubstage.php new file mode 100755 index 0000000000..29b58156ab --- /dev/null +++ b/module/programplan/test/ui/createsubstage.php @@ -0,0 +1,32 @@ +#!/usr/bin/env php +loadYaml('execution', false, 2)->gen(10); +$tester = new createsubstageTester(); +$tester->login(); + +$waterfall = array( + array('name_0' => '', 'begin_0' => date('Y-m-d'), 'end_0' => date('Y-m-d', strtotime('+30 days'))), + array('name_0' => '需求的子阶段1', 'begin_0' => '', 'end_0' => date('Y-m-d', strtotime('+30 days'))), + array('name_0' => '需求的子阶段1', 'begin_0' => date('Y-m-d'), 'end_0' => ''), + array('name_0' => '需求的子阶段1', 'begin_0' => '2020-11-10', 'end_0' => '2020-11-09'), + array('name_0' => '需求的子阶段1', 'begin_0' => '2020-11-11', 'end_0' => '2020-11-17'), +); + +r($tester->createSubStage($waterfall['0'])) && p('message,status') && e('创建阶段表单页提示信息正确,success'); //校验阶段名称不能为空 +r($tester->createSubStage($waterfall['1'])) && p('message,status') && e('创建阶段表单页提示信息正确,success'); //校验计划开始必填 +r($tester->createSubStage($waterfall['2'])) && p('message,status') && e('创建阶段表单页提示信息正确,success'); //校验计划完成必填 +r($tester->createSubStage($waterfall['3'])) && p('message,status') && e('创建阶段表单页提示信息正确,success'); //校验计划完成必须大于计划开始 +r($tester->createSubStage($waterfall['4'])) && p('status') && e('success'); //创建需求子阶段 + +$tester->closeBrowser(); diff --git a/module/programplan/test/ui/editstage.php b/module/programplan/test/ui/editstage.php new file mode 100755 index 0000000000..84ccad1114 --- /dev/null +++ b/module/programplan/test/ui/editstage.php @@ -0,0 +1,30 @@ +#!/usr/bin/env php +loadYaml('execution', false, 2)->gen(10); +$tester = new editStageTester(); +$tester->login(); + +$waterfall = array( + array('name' => '', 'begin' => '2020-11-02', 'end' => '2020-11-09'), + array('name' => '需求的阶段a1', 'begin' => '', 'end' => '2020-11-09'), + array('name' => '需求的阶段a1', 'begin' => '2020-11-02', 'end' => ''), + array('name' => '需求的阶段a1', 'begin' => '2020-11-11', 'end' => '2020-11-16'), +); + +r($tester->checkInput($waterfall['0'])) && p('message,status') && e('编辑阶段表单页提示信息正确,success'); //校验阶段名称不能为空 +r($tester->checkInput($waterfall['1'])) && p('message,status') && e('编辑阶段表单页提示信息正确,success'); //校验计划开始必填 +r($tester->checkInput($waterfall['2'])) && p('message,status') && e('编辑阶段表单页提示信息正确,success'); //校验计划完成必填 +r($tester->checkInput($waterfall['3'])) && p('status') && e('success'); //编辑需求阶段 + +$tester->closeBrowser(); diff --git a/module/programplan/test/ui/page/create.php b/module/programplan/test/ui/page/create.php index 06d4998054..a92a49962f 100644 --- a/module/programplan/test/ui/page/create.php +++ b/module/programplan/test/ui/page/create.php @@ -5,7 +5,19 @@ class createPage extends page { parent::__construct($webdriver); $xpath = array( - 'settings' => "//*[@id='navbar']/menu/li[14]/a/span", + 'settings' => "//*[@id='navbar']/menu/li[14]/a/span", + 'deleteBtn_2' => "//*[@id='zin_programplan_create_formBatch']/div[1]/table/tbody/tr[2]/td[19]/button[3]", + 'deleteBtn_3' => "//*[@id='zin_programplan_create_formBatch']/div[1]/table/tbody/tr[3]/td[19]/button[3]", + 'deleteBtn_4' => "//*[@id='zin_programplan_create_formBatch']/div[1]/table/tbody/tr[4]/td[19]/button[3]", + 'deleteBtn_5' => "//*[@id='zin_programplan_create_formBatch']/div[1]/table/tbody/tr[5]/td[19]/button[3]", + 'deleteBtn_6' => "//*[@id='zin_programplan_create_formBatch']/div[1]/table/tbody/tr[6]/td[19]/button[3]", + 'begin' => "//*[@id='begin_0']/div/input[2]", + 'end' => "//*[@id='end_0']/div/input[2]", + 'submitBtn' => "//*[@id='zin_programplan_create_formBatch']/div[2]/button", + 'beginTip' => "//*[@id='begin[1]Tip']", + 'endTip' => "//*[@id='end[1]Tip']", + 'nameTip' => "//*[@class='modal modal-async load-indicator modal-alert modal-trans show in']/div/div/div[2]/div", + 'confirmBtn' => "//*[@class='modal modal-async load-indicator modal-alert modal-trans show in']/div/div/div[3]/nav/button/span", ); $this->dom->xpath = array_merge($this->dom->xpath, $xpath); } diff --git a/module/programplan/test/ui/page/edit.php b/module/programplan/test/ui/page/edit.php new file mode 100644 index 0000000000..eac910b0ff --- /dev/null +++ b/module/programplan/test/ui/page/edit.php @@ -0,0 +1,15 @@ + "//*[@id='nameTip']", + 'beginTip' => "//*[@id='beginTip']", + 'endTip' => "//*[@id='endTip']", + 'submitBtn' => "//*[@id='zin_programplan_edit_form']/div[9]/div/button/span", + ); + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/programplan/test/yaml/execution.yaml b/module/programplan/test/yaml/execution.yaml new file mode 100644 index 0000000000..990b44ca83 --- /dev/null +++ b/module/programplan/test/yaml/execution.yaml @@ -0,0 +1,65 @@ +title: execution. +desc: execution data. +author: Wang Yidong +version: "1.0" +fields: + - field: id + range: 11,60,61,100,101-700 + - field: name + note: "名称" + fields: + - field: name1 + range: 敏捷项目,瀑布项目{2},看板项目,迭代{5},阶段{18},看板{5} + - field: name2 + range: 1-10000 + - field: project + range: 0{4},11{5},60{12},61{6},100{5} + - field: model + range: scrum,waterfall{2},kanban,[]{28} + - field: attribute + range: "[]{4},[]{5},request,design,dev,qa,release,review,request,design,dev,qa,release,review,request,design,dev,qa,release,review,[]{5}" + - field: type + range: project{4},sprint{5},stage{18},kanban{5} + - field: budget + range: 800000-1:100 + - field: status + range: doing{4},wait{2},doing{2},suspended{2},closed{2} + - field: auth + range: "extend" + - field: desc + range: 1-10000 + prefix: "迭代描述" + - field: begin + range: "20201101 000000:1D" + type: timestamp + format: "YYYY-MM-DD" + postfix: "\t" + - field: end + range: "20220101 000000:1D" + type: timestamp + format: "YYYY-MM-DD" + postfix: "\t" + - field: grade + range: 1{4},1{5},1{6},2{6},1{6},1{5} + - field: parent + range: 0{4},11{5},60{6},106-111,61{6},100{5} + - field: PM + range: 'admin,user1,user2,test1,test2' + - field: path + fields: + - field: path1 + prefix: "," + range: 11,60,61,100,11{5},60{12},61{6},100{5} + - field: path2 + prefix: "," + range: "[]{4},101-105,106-111,106-111,118-700" + postfix: "," + - field: path3 + range: "[]{4},[]{5},[]{6},112-117,[]{50}" + postfix: "," + - field: acl + range: open{4},private{4} + - field: multiple + range: 1{4},1{3},0{2},1{18},1{5} + - field: deleted + range: 0{30},1 diff --git a/module/programplan/test/yaml/project.yaml b/module/programplan/test/yaml/project.yaml new file mode 100644 index 0000000000..5bf140edf9 --- /dev/null +++ b/module/programplan/test/yaml/project.yaml @@ -0,0 +1,56 @@ +title: project. +desc: project data. +author: Sun Guangming +version: "1.0" +fields: + - field: id + range: 1-100 + - field: name + note: "名称" + fields: + - field: name1 + range: 敏捷项目,瀑布项目,看板项目 + - field: name2 + range: 1-100 + - field: project + range: 0 + - field: code + prefix: "code" + range: 1-1000 + - field: model + range: scrum,waterfall,kanban + - field: type + range: project + - field: budget + range: 800000-1:100 + - field: status + range: doing,wait,suspended,closed + - field: auth + range: "extend" + - field: desc + range: 1-10000 + prefix: "迭代描述" + - field: begin + range: "(-2M)-(+M):1D" + type: timestamp + format: "YYYY-MM-DD" + postfix: "\t" + - field: end + range: "(+1w)-(+2M):1D" + type: timestamp + format: "YYYY-MM-DD" + postfix: "\t" + - field: grade + range: 1 + - field: parent + range: 0 + - field: path + prefix: "," + range: 1-100 + postfix: "," + - field: hasProduct + range: 0,1 + - field: multiple + range: 1{10},0{10} + - field: storyType + range: "`story,requirement,epic`" diff --git a/module/programplan/ui/create.html.php b/module/programplan/ui/create.html.php index 96421067a4..c0f8d516d3 100644 --- a/module/programplan/ui/create.html.php +++ b/module/programplan/ui/create.html.php @@ -142,7 +142,7 @@ $fnGenerateSubPlanManageFields = function() use ($lang, $planID, $project, $exec $fnGenerateFields = function() use ($config, $lang, $requiredFields, $showFields, $fields, $PMUsers, $enableOptionalAttr, $programPlan, $planID, $executionType, $project) { $items = array(); - $items[] = $project->model == 'ipd' ? null : array('name' => 'id', 'label' => $lang->idAB, 'control' => 'index', 'width' => '32px'); + $items[] = $project->model == 'ipd' ? null : array('name' => 'index', 'label' => $lang->programplan->idAB, 'control' => 'index', 'width' => '40px'); $fields['attribute']['required'] = $fields['acl']['required'] = true; if(isset($requiredFields['code'])) $fields['code']['required'] = true; @@ -153,6 +153,11 @@ $fnGenerateFields = function() use ($config, $lang, $requiredFields, $showFields foreach($fields as $name => $field) { $field['name'] = $name; + if($name == 'id') + { + $field['control'] = 'hidden'; + $field['hidden'] = true; + } if(!empty($field['default'])) $field['value'] = $field['default']; /* Convert 'options' to 'items'. */ @@ -199,7 +204,7 @@ $fnGenerateDefaultData = function() use ($config, $plans, $planID, $stages, $exe { $items = array(); - /* Created a new project witho no stages. */ + /* Created a new project with no stages. */ if(empty($plans) && $planID == 0) { foreach($stages as $stage) @@ -269,7 +274,7 @@ jsVar('plans', $plans); jsVar('cropStageTip', $lang->programplan->cropStageTip); jsVar('ipdStagePoint', $project->model == 'ipd' ? $config->review->ipdReviewPoint : array()); jsVar('attributeList', $project->model == 'ipd' ? $lang->stage->ipdTypeList : $lang->stage->typeList); -jsvar('reviewedPoints', $project->model == 'ipd' ? $reviewedPoints : array()); +jsVar('reviewedPoints', $project->model == 'ipd' ? $reviewedPoints : array()); jsVar('reviewedPointTip', $project->model == 'ipd' ? $lang->programplan->reviewedPointTip : ''); featureBar(li @@ -291,6 +296,7 @@ toolbar formBatchPanel ( setID('dataform'), + set::idKey('index'), set::onRenderRow(jsRaw('window.onRenderRow')), to::headingActions(array($fnGenerateSubPlanManageFields())), set::customFields(array('list' => $customFields, 'show' => explode(',', $showFields), 'key' => 'createFields')), diff --git a/module/programplan/zen.php b/module/programplan/zen.php index 5e5141a186..c2d5dffc8c 100644 --- a/module/programplan/zen.php +++ b/module/programplan/zen.php @@ -64,7 +64,7 @@ class programplanZen extends programplan foreach($plans as $rowID => $plan) { if(empty($parentID) and empty($oldPlans)) $plan->id = ''; - $plan->days = helper::diffDate($plan->end, $plan->begin) + 1; + $plan->days = $this->calcDaysForStage($plan->begin, $plan->end); $plan->project = $projectID; $plan->parent = $parentID ? $parentID : $projectID; $plan->order = (int)array_shift($orders); @@ -101,6 +101,36 @@ class programplanZen extends programplan return $plans; } + /** + * 根据阶段的开始和结束,计算工作日。 + * Calc stage days by stage begin and end. + * + * @param string $start + * @param string $end + * @access public + * @return int + */ + public function calcDaysForStage($start, $end) + { + $weekend = $this->config->execution->weekend; + + $start = new DateTime($start); + $end = new DateTime($end); + $end->modify('+1 day'); // 包含结束日期 + + $interval = new DateInterval('P1D'); // 每次递增一天 + $dateRange = new DatePeriod($start, $interval, $end); + + $days = 0; + foreach($dateRange as $date) + { + if(($weekend == 2 && $date->format('N') == 6) || $date->format('N') == 7) continue; + $days++; + } + + return $days; + } + /** * 生成创建项目阶段视图数据。 * Build create view data. diff --git a/module/project/config/dtable.php b/module/project/config/dtable.php index 635f980b64..28a21c4a6e 100755 --- a/module/project/config/dtable.php +++ b/module/project/config/dtable.php @@ -55,7 +55,7 @@ $config->project->dtable->fieldList['PM']['link'] = array('module' => 'us $config->project->dtable->fieldList['PM']['data-toggle'] = 'modal'; $config->project->dtable->fieldList['PM']['data-size'] = 'sm'; $config->project->dtable->fieldList['PM']['group'] = 3; -$config->project->dtable->fieldList['PM']['required'] = true; +$config->project->dtable->fieldList['PM']['show'] = true; $config->project->dtable->fieldList['storyPoints']['title'] = $lang->project->storyPoints; $config->project->dtable->fieldList['storyPoints']['type'] = 'count'; diff --git a/module/project/control.php b/module/project/control.php index fe44b7e9bc..a5f8d94234 100755 --- a/module/project/control.php +++ b/module/project/control.php @@ -468,7 +468,7 @@ class project extends control if($this->post->longTime) $this->config->project->form->edit['end']['skipRequired'] = true; if($this->post->delta == '999') $this->config->project->form->edit['end']['skipRequired'] = true; - $postData = form::data($this->config->project->form->edit); + $postData = form::data($this->config->project->form->edit, $projectID); $postProductData = !empty($project->hasProduct) ? form::data($this->config->project->form->edit)->get('products,plans,branch') : new stdclass(); $newProject = $this->projectZen->prepareProject($postData, $project->hasProduct); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -1286,7 +1286,7 @@ class project extends control if(!empty($_POST)) { - $postData = form::data($this->config->project->form->start); + $postData = form::data($this->config->project->form->start, $projectID); $postData = $this->projectZen->prepareStartExtras($postData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $changes = $this->project->start($projectID, $postData); @@ -1314,7 +1314,7 @@ class project extends control /* Processing parameter passing while suspend project. */ if(!empty($_POST)) { - $postData = form::data($this->config->project->form->suspend); + $postData = form::data($this->config->project->form->suspend, $projectID); $postData = $this->projectZen->prepareSuspendExtras($projectID, $postData); /* Update the database status to suspended. */ @@ -1346,7 +1346,7 @@ class project extends control /* Processing parameter passing while close project. */ if(!empty($_POST)) { - $postData = form::data($this->config->project->form->close); + $postData = form::data($this->config->project->form->close, $projectID); $postData = $this->projectZen->prepareClosedExtras($projectID, $postData); /* Update the database status to closed. */ @@ -1379,7 +1379,7 @@ class project extends control if(!empty($_POST)) { - $postData = form::data($this->config->project->form->activate); + $postData = form::data($this->config->project->form->activate, $projectID); $postData = $this->projectZen->prepareActivateExtras($projectID, $postData); $changes = $this->project->activate($projectID, $postData); diff --git a/module/project/lang/de.php b/module/project/lang/de.php index fc7c053fa1..adf57cd6bb 100644 --- a/module/project/lang/de.php +++ b/module/project/lang/de.php @@ -485,3 +485,7 @@ $lang->project->executionList['waterfallplus'] = $lang->projectCommon . ' Stage' $lang->project->featureBar['team']['all'] = 'Members'; $lang->project->featureBar['managemembers']['all'] = 'Manage Team'; + +$lang->project->api = new stdclass(); +$lang->project->api->error = new stdclass(); +$lang->project->api->error->productNotFound = 'Product not found'; diff --git a/module/project/lang/en.php b/module/project/lang/en.php index fc7c053fa1..adf57cd6bb 100644 --- a/module/project/lang/en.php +++ b/module/project/lang/en.php @@ -485,3 +485,7 @@ $lang->project->executionList['waterfallplus'] = $lang->projectCommon . ' Stage' $lang->project->featureBar['team']['all'] = 'Members'; $lang->project->featureBar['managemembers']['all'] = 'Manage Team'; + +$lang->project->api = new stdclass(); +$lang->project->api->error = new stdclass(); +$lang->project->api->error->productNotFound = 'Product not found'; diff --git a/module/project/lang/fr.php b/module/project/lang/fr.php index fc7c053fa1..adf57cd6bb 100644 --- a/module/project/lang/fr.php +++ b/module/project/lang/fr.php @@ -485,3 +485,7 @@ $lang->project->executionList['waterfallplus'] = $lang->projectCommon . ' Stage' $lang->project->featureBar['team']['all'] = 'Members'; $lang->project->featureBar['managemembers']['all'] = 'Manage Team'; + +$lang->project->api = new stdclass(); +$lang->project->api->error = new stdclass(); +$lang->project->api->error->productNotFound = 'Product not found'; diff --git a/module/project/lang/zh-cn.php b/module/project/lang/zh-cn.php index 280751adf6..f14565d338 100644 --- a/module/project/lang/zh-cn.php +++ b/module/project/lang/zh-cn.php @@ -485,3 +485,7 @@ $lang->project->executionList['waterfallplus'] = $lang->projectCommon . '阶段' $lang->project->featureBar['team']['all'] = '团队成员'; $lang->project->featureBar['managemembers']['all'] = '团队管理'; + +$lang->project->api = new stdclass(); +$lang->project->api->error = new stdclass(); +$lang->project->api->error->productNotFound = '产品不存在'; diff --git a/module/project/model.php b/module/project/model.php index 97a1509442..7aceae529b 100755 --- a/module/project/model.php +++ b/module/project/model.php @@ -1666,7 +1666,7 @@ class projectModel extends model * @access public * @return void */ - public function unlinkStoryByType($projectID, $storyType) + public function unlinkStoryByType($projectID = 0, $storyType = '') { $idList = $this->dao->select('id')->from(TABLE_PROJECT) ->where('deleted')->eq(0) @@ -1677,11 +1677,11 @@ class projectModel extends model $storyIdList = $this->dao->select('t1.story')->from(TABLE_PROJECTSTORY)->alias('t1') ->leftJoin(TABLE_STORY)->alias('t2')->on('t1.story = t2.id') - ->where('t1.project')->in($idList) - ->andWhere('t2.type')->in($storyType) + ->where('t2.type')->in($storyType) + ->beginIF($projectID > 0)->andWhere('t1.project')->in($idList)->fi() ->fetchPairs(); - if($storyIdList) $this->dao->delete()->from(TABLE_PROJECTSTORY)->where('story')->in($storyIdList)->andWhere('project')->in($idList)->exec(); + if($storyIdList) $this->dao->delete()->from(TABLE_PROJECTSTORY)->where('story')->in($storyIdList)->beginIF($projectID > 0)->andWhere('project')->in($idList)->fi()->exec(); } /** @@ -1749,16 +1749,6 @@ class projectModel extends model $accounts = $this->projectTao->insertMember($members, $projectID, $oldJoin); $this->projectTao->updateMemberView($projectID, $accounts, $oldJoin); - /* Remove execution members. */ - if($this->post->removeExecution == 'yes' and !empty($childSprints) and !empty($removedAccounts)) - { - $this->dao->delete()->from(TABLE_TEAM) - ->where('root')->in($childSprints) - ->andWhere('type')->eq('execution') - ->andWhere('account')->in($removedAccounts) - ->exec(); - } - if(empty($project->multiple) and $project->model != 'waterfall') $this->loadModel('execution')->syncNoMultipleSprint($projectID); return !dao::isError(); diff --git a/module/project/tao.php b/module/project/tao.php index d9e469fa19..440175c816 100755 --- a/module/project/tao.php +++ b/module/project/tao.php @@ -266,7 +266,7 @@ class projectTao extends projectModel $childSprints = $this->dao->select('id')->from(TABLE_PROJECT) ->where('project')->eq($projectID) - ->andWhere('type')->in('stage,sprint') + ->andWhere('type')->in('stage,sprint,kanban') ->andWhere('deleted')->eq('0') ->fetchPairs(); @@ -280,6 +280,16 @@ class projectTao extends projectModel $this->loadModel('user')->updateUserView(array($projectID), 'project', $changedAccounts); if(!empty($childSprints)) $this->user->updateUserView($childSprints, 'sprint', $changedAccounts); if(!empty($linkedProducts)) $this->user->updateUserView(array_keys($linkedProducts), 'product', $changedAccounts); + + /* Remove execution members. */ + if($this->post->removeExecution == 'yes' and !empty($childSprints) and !empty($removedAccounts)) + { + $this->dao->delete()->from(TABLE_TEAM) + ->where('root')->in($childSprints) + ->andWhere('type')->eq('execution') + ->andWhere('account')->in($removedAccounts) + ->exec(); + } } /** @@ -788,7 +798,7 @@ class projectTao extends projectModel ->beginIF($status == 'undone')->andWhere('status')->notIN('done,closed')->fi() ->beginIF($status == 'unclosed')->andWhere('status')->ne('closed')->fi() ->beginIF($status && !in_array($status, array('all', 'undone', 'unclosed')))->andWhere('status')->eq($status)->fi() - ->beginIF($projectID)->andWhere('id')->eq($projectID)->fi() + ->beginIF($projectID)->orWhere('id')->eq($projectID)->fi() ->orderBy($orderBy) ->beginIF($limit)->limit($limit)->fi() ->fetchAll('id'); diff --git a/module/project/test/lib/createsprint.ui.class.php b/module/project/test/lib/createsprint.ui.class.php new file mode 100644 index 0000000000..ba74e09e9f --- /dev/null +++ b/module/project/test/lib/createsprint.ui.class.php @@ -0,0 +1,63 @@ +initForm('project', 'execution', array('status' => 'undone', 'projectID' => '1'), 'appIframe-project'); + $sprintForm->dom->addSprint->click(); + $form = $this->initForm('execution', 'create', array('projectID' => '1')); + if(isset($sprint['name'])) $form->dom->name->setValue($sprint['name']); + if(isset($sprint['project'])) $form->dom->project->picker($sprint['project']); + if(isset($sprint['end'])) $form->dom->end->datepicker($sprint['end']); + + $form->dom->btn($this->lang->save)->click(); + $form->wait(5); + return $this->checkResult($sprint); + } + + /** + * Check the result after creating the sprint. + * + * @param string $scrum + * @access public + * @return object + */ + public function checkResult($sprint) + { + //检查创建页面时的提示信息 + if(strpos($this->response('url'), 'executionID') === false) + { + if($this->checkFormTips('execution')) return $this->success('创建迭代表单页提示信息正确'); + if($form->dom->endTip) + { + //检查结束日期不能为空 + $endTiptext = $form->dom->endTip->getText(); + $endTip = sprintf($this->lang->copyProject->endTip, ''); + return ($endTiptext == $endTip) ? $this->success('创建迭代表单页提示信息正确') : $this->failed('创建迭代表单页提示信息不正确'); + form->wait(1); + } + return $this->failed('创建迭代表单页提示信息不正确'); + } + //检查创建成功后的断言 + else + { + $sprintForm = $this->initForm('project', 'execution', array('status' => 'undone', 'projectID' => '1'), 'appIframe-project'); + /* 跳转到项目迭代列表页面,查看列表中的迭代信息是否正确*/ + $browsePage = $this->loadPage('project', 'execution'); + + //断言检查名称、项目类型是否正确 + if($browsePage->dom->sprintName->getText() != $sprint['name']) return $this->failed('名称错误'); + if($browsePage->dom->planEnd->getText() != $sprint['end']) return $this->failed('名称错误'); + + return $this->success(); + } + } +} diff --git a/module/project/test/tao/fetchprojectlistbyquery.php b/module/project/test/tao/fetchprojectlistbyquery.php index f512762e1a..0c46a43819 100755 --- a/module/project/test/tao/fetchprojectlistbyquery.php +++ b/module/project/test/tao/fetchprojectlistbyquery.php @@ -4,14 +4,15 @@ /** title=测试 projectModel::fetchProjectListByQuery($status, $projectID, $orderBy); +timeout=0 cid=1 - 获取未开始的项目数量 @3 - 获取状态不为done和closed的项目数量 @7 -- 根据项目ID获取项目 @1 -- 获取不存在的项目 @0 -- 根据 不匹配的项目ID和状态 获取项目数量 @0 -- 根据 匹配的项目ID和状态 获取项目数量 @1 +- 根据项目ID获取项目 @9 +- 获取不存在的项目 @9 +- 根据 不匹配的项目ID和状态 获取项目数量 @3 +- 根据 匹配的项目ID和状态 获取项目数量 @2 - 按照ID正序获取项目列表,查看排第一个的项目详情 - 属性id @11 - 属性name @项目11 @@ -63,11 +64,11 @@ $projectTester = new Project(); r(count($projectTester->testFetchProjectListByQuery('wait'))) && p() && e('3'); // 获取未开始的项目数量 r(count($projectTester->testFetchProjectListByQuery('undone'))) && p() && e('7'); // 获取状态不为done和closed的项目数量 -r(count($projectTester->testFetchProjectListByQuery('', 11))) && p() && e('1'); // 根据项目ID获取项目 -r(count($projectTester->testFetchProjectListByQuery('', 10000))) && p() && e('0'); // 获取不存在的项目 +r(count($projectTester->testFetchProjectListByQuery('', 11))) && p() && e('9'); // 根据项目ID获取项目 +r(count($projectTester->testFetchProjectListByQuery('', 10000))) && p() && e('9'); // 获取不存在的项目 -r(count($projectTester->testFetchProjectListByQuery('doing', 11))) && p() && e('0'); // 根据 不匹配的项目ID和状态 获取项目数量 -r(count($projectTester->testFetchProjectListByQuery('doing', 12))) && p() && e('1'); // 根据 匹配的项目ID和状态 获取项目数量 +r(count($projectTester->testFetchProjectListByQuery('doing', 11))) && p() && e('3'); // 根据 不匹配的项目ID和状态 获取项目数量 +r(count($projectTester->testFetchProjectListByQuery('doing', 12))) && p() && e('2'); // 根据 匹配的项目ID和状态 获取项目数量 r(current($projectTester->testFetchProjectListByQuery('all', 0, 'id_asc'))) && p('id,name') && e('11,项目11'); // 按照ID正序获取项目列表,查看排第一个的项目详情 -r(current($projectTester->testFetchProjectListByQuery('all', 0, 'name_desc'))) && p('id,name') && e('19,项目19'); // 按照项目名称倒序获取项目列表,查看排第一个的项目详情 +r(current($projectTester->testFetchProjectListByQuery('all', 0, 'name_desc'))) && p('id,name') && e('19,项目19'); // 按照项目名称倒序获取项目列表,查看排第一个的项目详情 \ No newline at end of file diff --git a/module/project/test/ui/createsprint.php b/module/project/test/ui/createsprint.php new file mode 100755 index 0000000000..b9f2848426 --- /dev/null +++ b/module/project/test/ui/createsprint.php @@ -0,0 +1,28 @@ +#!/usr/bin/env php +loadYaml('project', false, 1)->gen(1); +$tester = new createSprintTester(); +$tester->login(); + +//设置敏捷项目执行数据 +$sprint = array( + array('project' => '敏捷项目1', 'name' => '', 'end' => date('Y-m-d', strtotime('+5 days'))), + array('project' => '敏捷项目1', 'name' => '一个敏捷迭代' . time(), 'end' => ''), + array('project' => '敏捷项目1', 'name' => '一个敏捷迭代' . time(), 'end' => date('Y-m-d', strtotime('+5 days'))), +); + +r($tester->checkInput($sprint['0'])) && p('message') && e('创建执行成功'); //执行名称为空时的提示校验 +r($tester->checkInput($sprint['1'])) && p('message') && e('创建执行成功'); //计划完成时间为空时的提示校验 +r($tester->checkInput($sprint['2'])) && p('status') && e('SUCCESS'); //添加迭代成功 + +$tester->closeBrowser(); diff --git a/module/project/test/ui/page/execution.php b/module/project/test/ui/page/execution.php new file mode 100644 index 0000000000..84c6644c6b --- /dev/null +++ b/module/project/test/ui/page/execution.php @@ -0,0 +1,23 @@ + "//*[@id='table-project-execution']/div[2]/div[1]/div/div[2]/div/div/div/a", + 'begin' => "//*[@id='table-project-execution']/div[2]/div[2]/div/div[3]/div", + 'end' => "//*[@id='table-project-execution']/div[2]/div[2]/div/div[4]/div", + 'subName' => "//*[@id='table-project-execution']/div[2]/div[1]/div/div[4]/div/div[2]/div/a", + 'subBegin' => "//*[@id='table-project-execution']/div[2]/div[2]/div/div[14]/div", + 'subEnd' => "//*[@id='table-project-execution']/div[2]/div[2]/div/div[15]/div", + 'editBtn' => "//*[@id='table-project-execution']/div[2]/div[3]/div/div/div/nav/a[3]/i", + 'submitBtn' => "//*[@id='zin_programplan_edit_form']/div[9]/div/button/span", + 'planBegin' => "//*[@id='table-project-execution']/div[2]/div[2]/div/div[4]/div", + 'planEnd' => "//*[@id='table-project-execution']/div[2]/div[2]/div/div[5]/div", + 'addSprint' => "//*[@id='actionBar']/a[2]/span", + 'sprintName' => "//*[@id='mainContent']/div/div/div[2]/div/div/div[2]/div/div/div/a", + ); + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/project/test/ui/yaml/project.yaml b/module/project/test/ui/yaml/project.yaml new file mode 100644 index 0000000000..d5877666a6 --- /dev/null +++ b/module/project/test/ui/yaml/project.yaml @@ -0,0 +1,45 @@ +title: project. +desc: project data. +author: Li Huifang +version: "1.0" +fields: + - field: id + range: 1 + - field: name + note: "名称" + fields: + - field: name + range: 敏捷项目1 + - field: project + range: 0 + - field: code + prefix: "code" + range: 1 + - field: model + range: scrum + - field: type + range: project + - field: budget + range: 800000-1:100 + - field: status + range: doing + - field: begin + range: "(-2M)-(+M):1D" + type: timestamp + format: "YYYY-MM-DD" + postfix: "\t" + - field: end + range: "(+1w)-(+2M):1D" + type: timestamp + format: "YYYY-MM-DD" + postfix: "\t" + - field: grade + range: 1 + - field: parent + range: 0 + - field: path + prefix: "," + range: 1 + postfix: "," + - field: multiple + range: 1 diff --git a/module/project/zen.php b/module/project/zen.php index c922f32259..146daf4995 100755 --- a/module/project/zen.php +++ b/module/project/zen.php @@ -153,6 +153,12 @@ class projectZen extends project $multipleProducts = $this->loadModel('product')->getMultiBranchPairs($topProgramID); foreach($rawdata->products as $index => $productID) { + if(defined('RUN_MODE') && RUN_MODE == 'api') + { + $product = $this->loadModel('product')->getById($productID); + if(empty($product)) dao::$errors['products'][$index][$productID] = $this->lang->project->api->error->productNotFound; + } + if(isset($multipleProducts[$productID])) { foreach($rawdata->branch[$index] as $branchID) @@ -1012,7 +1018,6 @@ class projectZen extends project ->setDefault('lastEditedDate', helper::now()) ->setIF($rawdata->begin == '0000-00-00', 'begin', '') ->setIF($rawdata->end == '0000-00-00', 'end', '') - ->setIF(!helper::isZeroDate($oldProject->realBegan), 'realBegan', helper::today()) ->stripTags($editorIdList, $this->config->allowedTags) ->get(); } diff --git a/module/projectbuild/control.php b/module/projectbuild/control.php index f1e85cfbc0..abb5783480 100644 --- a/module/projectbuild/control.php +++ b/module/projectbuild/control.php @@ -42,6 +42,9 @@ class projectBuild extends control { $executionID = 0; $project = $this->loadModel('project')->fetchById($projectID); + + if($project->model == 'waterfall') $this->lang->projectbuild->execution = $this->lang->project->stage; + if(strpos('stage,sprint,kanban', $project->type) !== false) { $executionID = $projectID; diff --git a/module/projectbuild/lang/de.php b/module/projectbuild/lang/de.php index 8dad84b184..3ddc80aa5b 100644 --- a/module/projectbuild/lang/de.php +++ b/module/projectbuild/lang/de.php @@ -23,6 +23,8 @@ $lang->projectbuild->batchUnlink = 'Batch Unlink'; $lang->projectbuild->batchUnlinkStory = "Batch Unlink {$lang->SRCommon}"; $lang->projectbuild->batchUnlinkBug = 'Batch Unlink Bug'; $lang->projectbuild->name = 'Name'; +$lang->projectbuild->execution = $lang->executionCommon; +$lang->projectbuild->product = $lang->productCommon; $lang->projectbuild->scmPath = 'SCM Path'; $lang->projectbuild->filePath = 'File Path'; $lang->projectbuild->desc = 'Description'; diff --git a/module/projectbuild/lang/en.php b/module/projectbuild/lang/en.php index 8dad84b184..3ddc80aa5b 100644 --- a/module/projectbuild/lang/en.php +++ b/module/projectbuild/lang/en.php @@ -23,6 +23,8 @@ $lang->projectbuild->batchUnlink = 'Batch Unlink'; $lang->projectbuild->batchUnlinkStory = "Batch Unlink {$lang->SRCommon}"; $lang->projectbuild->batchUnlinkBug = 'Batch Unlink Bug'; $lang->projectbuild->name = 'Name'; +$lang->projectbuild->execution = $lang->executionCommon; +$lang->projectbuild->product = $lang->productCommon; $lang->projectbuild->scmPath = 'SCM Path'; $lang->projectbuild->filePath = 'File Path'; $lang->projectbuild->desc = 'Description'; diff --git a/module/projectbuild/lang/fr.php b/module/projectbuild/lang/fr.php index 8dad84b184..3ddc80aa5b 100644 --- a/module/projectbuild/lang/fr.php +++ b/module/projectbuild/lang/fr.php @@ -23,6 +23,8 @@ $lang->projectbuild->batchUnlink = 'Batch Unlink'; $lang->projectbuild->batchUnlinkStory = "Batch Unlink {$lang->SRCommon}"; $lang->projectbuild->batchUnlinkBug = 'Batch Unlink Bug'; $lang->projectbuild->name = 'Name'; +$lang->projectbuild->execution = $lang->executionCommon; +$lang->projectbuild->product = $lang->productCommon; $lang->projectbuild->scmPath = 'SCM Path'; $lang->projectbuild->filePath = 'File Path'; $lang->projectbuild->desc = 'Description'; diff --git a/module/projectbuild/lang/zh-cn.php b/module/projectbuild/lang/zh-cn.php index 9d4880edd6..cd2077ca31 100644 --- a/module/projectbuild/lang/zh-cn.php +++ b/module/projectbuild/lang/zh-cn.php @@ -23,6 +23,8 @@ $lang->projectbuild->batchUnlink = '批量移除'; $lang->projectbuild->batchUnlinkStory = "批量移除{$lang->SRCommon}"; $lang->projectbuild->batchUnlinkBug = '批量移除Bug'; $lang->projectbuild->name = '名称编号'; +$lang->projectbuild->execution = $lang->executionCommon; +$lang->projectbuild->product = '所属' . $lang->productCommon; $lang->projectbuild->scmPath = '源代码地址'; $lang->projectbuild->filePath = '下载地址'; $lang->projectbuild->desc = '描述'; diff --git a/module/projectrelease/control.php b/module/projectrelease/control.php index 95d1ab8a98..8dbb3cbd86 100644 --- a/module/projectrelease/control.php +++ b/module/projectrelease/control.php @@ -180,7 +180,7 @@ class projectrelease extends control $this->config->release->edit->requiredFields = str_replace(',date', '', $this->config->release->edit->requiredFields); } - $releaseData = form::data($this->config->release->form->edit)->setIF($this->post->build === false, 'build', 0)->get(); + $releaseData = form::data($this->config->release->form->edit, $releaseID)->setIF($this->post->build === false, 'build', 0)->get(); $changes = $this->release->update($releaseData, $release); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); if($changes) diff --git a/module/projectrelease/test/lib/createprojectrelease.ui.class.php b/module/projectrelease/test/lib/createprojectrelease.ui.class.php new file mode 100644 index 0000000000..f8cf1ddf10 --- /dev/null +++ b/module/projectrelease/test/lib/createprojectrelease.ui.class.php @@ -0,0 +1,53 @@ +initForm('projectrelease', 'create', array('projectID' => 1), 'appIframe-project'); + if(isset($release['name'])) $form->dom->name->setValue($release['name']); + if(isset($release['status'])) $form->dom->status->picker($release['status']); + $form->wait(2); + if(isset($release['plandate'])) $form->dom->date->datepicker($release['plandate']); + if(isset($release['releasedate'])) $form->dom->releasedDate->datepicker($release['releasedate']); + + $form->dom->btn($this->lang->save)->click(); + $form->wait(5); + return $this->checkResult($release); + } + + /** + * Check the result after creating the project release. + * 创建项目发布后检查结果 + * + * @param array $release + * @access public + * @return object + */ + public function checkResult($release) + { + //检查创建页面时的提示信息 + if($this->response('method') != 'view') + { + if($this->checkFormTips('projectrelease')) return $this->success('创建项目发布表单页提示信息正确'); + return $this->failed('创建项目发布表单页提示信息不正确'); + } + /* 跳转到发布概况页面,点击基本信息标签,查看信息是否正确 */ + $viewPage = $this->loadPage('projectrelease', 'view'); + $viewPage->dom->basic->click(); + $viewPage->wait(5); + + //断言检查发布名称、状态是否正确 + if($viewPage->dom->basicreleasename->getText() != $release['name']) return $this->failed('项目发布名称错误'); + if($viewPage->dom->basicstatus->getText() != $release['status']) return $this->failed('项目发布状态错误'); + + return $this->success(); + } +} diff --git a/module/projectrelease/test/ui/createprojectrelease.php b/module/projectrelease/test/ui/createprojectrelease.php new file mode 100644 index 0000000000..ac19c7d84c --- /dev/null +++ b/module/projectrelease/test/ui/createprojectrelease.php @@ -0,0 +1,26 @@ +#!/usr/bin/env php +login(); + +//设置项目发布数据 +$release = array( + array('name' => ''), + array('name' => '一个未开始的项目发布'.time(), 'status' => '未开始', 'plandate' => date('Y-m-d', strtotime('+1 day'))), + array('name' => '一个已发布的项目发布'.time(), 'status' => '已发布', 'plandate' => date('Y-m-d', strtotime('+1 day')), 'releasedate' => date('Y-m-d', strtotime('+1 month'))), +); + +r($tester->checkInput($release['0'],$project)) && p('message') && e('创建敏捷项目表单页提示信息正确'); // 缺少发布名称,检查提示信息 +r($tester->checkInput($release['1'],$project)) && p('status') && e('SUCCESS'); // 创建状态为未开始的发布 +r($tester->checkInput($release['2'],$project)) && p('status') && e('SUCCESS'); // 创建状态为已发布的发布 + +$tester->closeBrowser(); diff --git a/module/programplan/test/ui/page/craete.php b/module/projectrelease/test/ui/page/create.php similarity index 76% rename from module/programplan/test/ui/page/craete.php rename to module/projectrelease/test/ui/page/create.php index b2110e8b0b..589c0fbf75 100644 --- a/module/programplan/test/ui/page/craete.php +++ b/module/projectrelease/test/ui/page/create.php @@ -5,7 +5,6 @@ class createPage extends page { parent::__construct($webdriver); $xpath = array( - 'settings' => "//*[@id='navbar']//a[@data-id='settings']/span" ); $this->dom->xpath = array_merge($this->dom->xpath, $xpath); } diff --git a/module/projectrelease/test/ui/page/view.php b/module/projectrelease/test/ui/page/view.php new file mode 100644 index 0000000000..1ec8f26d12 --- /dev/null +++ b/module/projectrelease/test/ui/page/view.php @@ -0,0 +1,14 @@ + "//*[@id='mainContent']/div[2]/div/div/div/div/ul/li[4]/a/span", + 'basicreleasename' => "//*[@id='releaseInfo']/div[2]/div[1]/table/tbody/tr[2]/td", + 'basicstatus' => "//*[@id='releaseInfo']/div[2]/div[1]/table/tbody/tr[4]/td", + ); + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/qa/model.php b/module/qa/model.php index abc642d882..03c4da0e31 100644 --- a/module/qa/model.php +++ b/module/qa/model.php @@ -1,7 +1,7 @@ config->release->edit->requiredFields = str_replace(',date', '', $this->config->release->edit->requiredFields); } - $releaseData = form::data()->setIF($this->post->build === false, 'build', 0)->get(); + $releaseData = form::data(null, $releaseID)->setIF($this->post->build === false, 'build', 0)->get(); $changes = $this->release->update($releaseData, $release); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); diff --git a/module/release/model.php b/module/release/model.php index a24bb759c7..80f705afd0 100755 --- a/module/release/model.php +++ b/module/release/model.php @@ -231,7 +231,7 @@ class releaseModel extends model $shadowBuild = new stdclass(); $shadowBuild->product = $release->product; $shadowBuild->branch = $release->branch; - $shadowBuild->project = (int)$release->project; + $shadowBuild->project = isset($release->project) ? (int)$release->project : 0; $shadowBuild->builds = $release->build; $shadowBuild->name = $release->name; $shadowBuild->date = $release->date; @@ -431,14 +431,33 @@ class releaseModel extends model $openedByList = $this->dao->select('openedBy')->from(TABLE_STORY)->where('id')->in($stories)->fetchPairs(); $notifyPersons += $openedByList; } - elseif(($notify == 'ET' || $notify == 'PT') && !empty($release->build)) + elseif($notify == 'ET' && !empty($release->build)) { - $type = $notify == 'ET' ? 'execution' : 'project'; - $table = $notify == 'ET' ? TABLE_BUILD : TABLE_RELEASE; - $members = $this->dao->select('t2.account')->from($table)->alias('t1') - ->leftJoin(TABLE_TEAM)->alias('t2')->on("t1.$type=t2.root") - ->where('t1.id')->in($notify == 'ET' ? $release->build : $release->id) - ->andWhere('t2.type')->eq($notify == 'ET' ? 'execution' : 'project') + $releaseBuilds = $this->dao->select('id, builds')->from(TABLE_BUILD)->where('id')->in($release->build)->fetchAll('id'); + + $allBuilds = array_keys($releaseBuilds); + foreach($releaseBuilds as $releaseBuild) + { + if(empty($releaseBuild->builds)) continue; + + $allBuilds = array_merge($allBuilds, explode(',', $releaseBuild->builds)); + } + + $members = $this->dao->select('t2.account')->from(TABLE_BUILD)->alias('t1') + ->leftJoin(TABLE_TEAM)->alias('t2')->on('t2.root = t1.execution') + ->where('t1.id')->in(array_filter(array_unique($allBuilds))) + ->andWhere('t2.type')->eq('execution') + ->fetchPairs(); + if(empty($members)) continue; + + $notifyPersons += $members; + } + elseif($notify == 'PT' && !empty($release->build)) + { + $members = $this->dao->select('t2.account')->from(TABLE_RELEASE)->alias('t1') + ->leftJoin(TABLE_TEAM)->alias('t2')->on("FIND_IN_SET(t2.root, t1.project)") + ->where('t1.id')->eq($release->id) + ->andWhere('t2.type')->eq('project') ->fetchPairs(); if(empty($members)) continue; diff --git a/module/release/test/ui/createrelease.php b/module/release/test/ui/createrelease.php new file mode 100644 index 0000000000..a8435208bf --- /dev/null +++ b/module/release/test/ui/createrelease.php @@ -0,0 +1,26 @@ +#!/usr/bin/env php +login(); + +$releaseName = '测试发布'; +$releaseStatus = array(); +$releaseStatus['wait'] = '未发布'; +$releaseStatus['release'] = '已发布'; + +r($tester->createRelease($releaseName, $releaseStatus('wait'))) && p('status') && e('SUCCESS'); diff --git a/module/repo/js/ajaxgeteditorcontent.ui.js b/module/repo/js/ajaxgeteditorcontent.ui.js index 72f4b7320c..10e4444045 100644 --- a/module/repo/js/ajaxgeteditorcontent.ui.js +++ b/module/repo/js/ajaxgeteditorcontent.ui.js @@ -187,14 +187,6 @@ function showCommitInfo() }) } -/* 初始化数据 */ -$(function() -{ - setTimeout(() => { - initPage(); - }, 200); -}); - /** * 初始化页面。 * Init page. @@ -202,7 +194,9 @@ $(function() * @access public * @return void */ -function initPage() + +/* 初始化数据 */ +window.afterPageUpdate = function initPage() { codeHeight = $.cookie.get('codeContainerHeight'); $('#codeContainer').css('height', $.cookie.get('codeContainerHeight')); diff --git a/module/repo/js/browse.ui.js b/module/repo/js/browse.ui.js index bbbf7a163b..5976efe7f4 100644 --- a/module/repo/js/browse.ui.js +++ b/module/repo/js/browse.ui.js @@ -5,7 +5,7 @@ window.renderCell = function(result, {col, row}) { if(col.name === 'name') { - var iconHtml = ''; + const iconHtml = ''; result[0] = {html: iconHtml + '' + row.data.name + '', className: row.data.account ? '' : 'hidden'}; return result; diff --git a/module/repo/js/common.ui.js b/module/repo/js/common.ui.js index df05f85da2..3fd0a471a7 100644 --- a/module/repo/js/common.ui.js +++ b/module/repo/js/common.ui.js @@ -3,14 +3,19 @@ $(function() $('#log').on('click', '.btn-close', closeRelation); window.onScmChange(); - $('#monacoTabs').on('show.zui.tab', function(e, tab) + setTimeout(function() { - $('#monacoTree .listitem').removeClass('selected'); + $('#monacoTabs').on('show.zui.tab', function(e, tab) + { + if($(e.target).hasClass('monaco-dropmenu')) return; - if(!tab || !tab[tab.length - 1]) return; - const fileKey = tab[tab.length - 1].substring(5).replace(/-/g, '='); - $('li[z-key="' + fileKey + '"] .listitem').addClass('selected'); - }); + $('#monacoTree .listitem').removeClass('selected'); + if(!tab || !tab[tab.length - 1] || typeof tab[tab.length - 1] != 'string') return; + + const fileKey = tab[tab.length - 1].substring(5).replace(/-/g, '='); + $('li[z-key="' + fileKey + '"] .listitem').addClass('selected'); + }); + }, 1000); }); /** diff --git a/module/repo/js/diff.ui.js b/module/repo/js/diff.ui.js index 7ce960650f..6f78fb8760 100644 --- a/module/repo/js/diff.ui.js +++ b/module/repo/js/diff.ui.js @@ -216,7 +216,7 @@ $('.inline-appose').on('click', function() return; }); -$(".label-exchange").on('click', function() +window.changeDiff = function() { var source = $('#oldRevision').val(); var target = $('#newRevision').val(); @@ -226,7 +226,7 @@ $(".label-exchange").on('click', function() $('#newRevision').val(source); window.goDiff(); } -}); +} /** * 在当前页面用modal加载链接。 diff --git a/module/repo/js/monaco.ui.js b/module/repo/js/monaco.ui.js index 7235578b36..26fd0f7f36 100644 --- a/module/repo/js/monaco.ui.js +++ b/module/repo/js/monaco.ui.js @@ -63,9 +63,9 @@ $('body').off('click', '.dropmenu-tree .dropmenu-item').on('click', '.dropmenu-t openUrl(currentLink, {app: appTab}); }) -$('.repoDropDownMenu').on('click', function() +window.dropdownClick = function(event) { - const url = $(this).data('link'); + const url = $(event).data('link'); const file = $('#monacoTabs .nav-item .active').attr('href').substring(5).replace(/-/g, '='); if(url.indexOf('blame') >=0 && url.indexOf('download') == -1) { @@ -75,8 +75,9 @@ $('.repoDropDownMenu').on('click', function() { window.open(url.replace('{path}', file), '_self'); } + return; -}) +} /** * 打开新tab。 diff --git a/module/repo/lang/de.php b/module/repo/lang/de.php index 6f77324e59..4361b2a48b 100644 --- a/module/repo/lang/de.php +++ b/module/repo/lang/de.php @@ -199,7 +199,7 @@ if(!$config->inQuickon) $lang->repo->scmList['Subversion'] = 'SVN'; } -$lang->repo->aclList['private'] = 'Private(The product and related project personnel can access it)'; +$lang->repo->aclList['private'] = 'Private(The product personnel can access it)'; $lang->repo->aclList['open'] = 'Open(Users with privileges to DevOps can access it)'; $lang->repo->aclList['custom'] = 'Custom'; diff --git a/module/repo/lang/en.php b/module/repo/lang/en.php index 626b4b4273..afc9492e99 100644 --- a/module/repo/lang/en.php +++ b/module/repo/lang/en.php @@ -199,7 +199,7 @@ if(!$config->inQuickon) $lang->repo->scmList['Subversion'] = 'SVN'; } -$lang->repo->aclList['private'] = 'Private(The product and related project personnel can access it)'; +$lang->repo->aclList['private'] = 'Private(The product personnel can access it)'; $lang->repo->aclList['open'] = 'Open(Users with privileges to DevOps can access it)'; $lang->repo->aclList['custom'] = 'Custom'; diff --git a/module/repo/lang/fr.php b/module/repo/lang/fr.php index ea4acf4513..3a41d52f9f 100644 --- a/module/repo/lang/fr.php +++ b/module/repo/lang/fr.php @@ -199,7 +199,7 @@ if(!$config->inQuickon) $lang->repo->scmList['Subversion'] = 'Subversion'; } -$lang->repo->aclList['private'] = 'Private(The product and related project personnel can access it)'; +$lang->repo->aclList['private'] = 'Private(The product personnel can access it)'; $lang->repo->aclList['open'] = 'Open(Users with privileges to DevOps can access it)'; $lang->repo->aclList['custom'] = 'Custom'; diff --git a/module/repo/lang/zh-cn.php b/module/repo/lang/zh-cn.php index 6bf8e205e3..314a60b505 100644 --- a/module/repo/lang/zh-cn.php +++ b/module/repo/lang/zh-cn.php @@ -199,7 +199,7 @@ if(!$config->inQuickon) $lang->repo->scmList['Subversion'] = 'Subversion'; } -$lang->repo->aclList['private'] = '私有 (所属产品和相关项目人员可访问)'; +$lang->repo->aclList['private'] = '私有 (所属产品人员可访问)'; $lang->repo->aclList['open'] = '公开 (有DevOps视图权限即可访问)'; $lang->repo->aclList['custom'] = '自定义'; diff --git a/module/repo/model.php b/module/repo/model.php index 5acb23b035..cb0a0fa39c 100644 --- a/module/repo/model.php +++ b/module/repo/model.php @@ -33,14 +33,9 @@ class repoModel extends model if($acl == 'private') { - $userProjects = explode(',', $this->app->user->view->projects); $userProducts = explode(',', $this->app->user->view->products); - $repoProjects = explode(',', $repo->projects); $repoProducts = explode(',', $repo->product); - - $sameProjects = array_intersect($userProjects, $repoProjects); - $sameProducts = array_intersect($userProducts, $repoProducts); - if(!empty($sameProjects) || !empty($sameProducts)) return true; + if(!array_intersect($userProducts, $repoProducts)) return true; } if(!empty($repo->acl->groups)) diff --git a/module/repo/test/lib/repo.ui.class.php b/module/repo/test/lib/repo.ui.class.php index 422535cd41..3ef42d236d 100644 --- a/module/repo/test/lib/repo.ui.class.php +++ b/module/repo/test/lib/repo.ui.class.php @@ -14,4 +14,20 @@ class repo extends tester $form->dom->{'product[]'}->multiPicker($maintain['product']); $form->dom->desc->setValue($maintain['desc']); } + + public function maintain() + { + $this->login(); + $form = $this->initForm('repo', 'maintain', '', 'appIframe-devops'); + $fieldXpath = $form->dom->getElementList($form->dom->xpath['fieldList']); + $fieldLists = array_map(function($element){return $element->getText();}, $fieldXpath->element); + $name = $this->lang->repo->name; + $product = '关联产品'; + $type = $this->lang->typeAB; + $lastSubmitTime = $this->lang->repo->lastSubmitTime; + $actions = $this->lang->actions; + $field = array($name, $product, $type, $lastSubmitTime, $actions); + if(empty(array_diff($fieldLists, $field))) return $this->success('代码库列表无误'); + return $this->failed('代码库列表有误'); + } } diff --git a/module/repo/test/ui/maintain.php b/module/repo/test/ui/maintain.php new file mode 100644 index 0000000000..150cc03b6f --- /dev/null +++ b/module/repo/test/ui/maintain.php @@ -0,0 +1,8 @@ +#!/usr/bin/env php +loadYaml('repo', false, 2)->gen(5); +$tester = new repo(); +r($tester->maintain()) && p('message,status') && e('代码库列表展示成功,SUCCESS'); //检查代码库页面 diff --git a/module/repo/test/ui/page/maintain.php b/module/repo/test/ui/page/maintain.php new file mode 100644 index 0000000000..f692798b87 --- /dev/null +++ b/module/repo/test/ui/page/maintain.php @@ -0,0 +1,13 @@ + '//*[@id="table-repo-maintain"]/div[1]//*[text()]' + ); + + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/repo/ui/diff.html.php b/module/repo/ui/diff.html.php index 9254e0aa67..de29c0f9bd 100644 --- a/module/repo/ui/diff.html.php +++ b/module/repo/ui/diff.html.php @@ -138,6 +138,7 @@ else } div( setClass($inModal ? 'hidden' : ''), + on::click('.label-exchange')->call('changeDiff'), \zin\featureBar ( backBtn(set::icon('back'), setClass('bg-transparent diff-back-btn'), set::back('GLOBAL'), $lang->goback), diff --git a/module/repo/ui/diffeditor.html.php b/module/repo/ui/diffeditor.html.php index 10e7140e7a..374927c6da 100644 --- a/module/repo/ui/diffeditor.html.php +++ b/module/repo/ui/diffeditor.html.php @@ -91,8 +91,8 @@ $inModal ? null : sidebar set::id('monacoTree'), set::items($tree), set::canSplit(false), - set::collapsedIcon('folder'), - set::expandedIcon('folder-open'), + set::collapsedIcon('folder text-warning'), + set::expandedIcon('folder-open text-warning'), set::normalIcon('file-text-alt'), set::selected($currentEntry), set::onClickItem(jsRaw('window.treeClick')) diff --git a/module/repo/ui/monaco.html.php b/module/repo/ui/monaco.html.php index 89bf606725..1f158b0a04 100644 --- a/module/repo/ui/monaco.html.php +++ b/module/repo/ui/monaco.html.php @@ -58,6 +58,7 @@ div( set::id('monacoTabs'), set::className('relative'), on::click('.monaco-close')->call('closeTab', jsRaw('this')), + on::click('.repoDropDownMenu')->call('dropdownClick', jsRaw('this')), div(setStyle(array('position' => 'absolute', 'width' => '100%', 'height' => '40px', 'background' => '#efefef', 'top' => '0px'))), tabPane ( @@ -117,8 +118,8 @@ $inModal ? null : sidebar set::id('monacoTree'), set::items($tree), set::canSplit(false), - set::collapsedIcon('folder'), - set::expandedIcon('folder-open'), + set::collapsedIcon('folder text-warning'), + set::expandedIcon('folder-open text-warning'), set::normalIcon('file-text-alt'), set::selected($file), set::onClickItem(jsRaw('window.treeClick')) diff --git a/module/search/config.php b/module/search/config.php index cbb4efd6aa..fc5f5d129f 100644 --- a/module/search/config.php +++ b/module/search/config.php @@ -183,3 +183,8 @@ else { $config->search->searchObject = array('trash'); } + +$config->search->controlProps = array(); +$config->search->controlProps['executionCase']['lastRunResult'] = (object)array('emptyValue' => ''); +$config->search->controlProps['testtask']['lastRunResult'] = (object)array('emptyValue' => ''); +$config->search->controlProps['testcase']['lastRunResult'] = (object)array('emptyValue' => ''); diff --git a/module/search/ui/buildform.html.php b/module/search/ui/buildform.html.php index 70a24834e2..db2beee9e3 100644 --- a/module/search/ui/buildform.html.php +++ b/module/search/ui/buildform.html.php @@ -16,6 +16,7 @@ foreach($fieldParams as $name => $param) $condition->name = $name; $condition->control = $param['control']; $condition->defaultOperator = $param['operator']; + if(isset($config->search->controlProps[$module][$name])) $condition->controlProps = $config->search->controlProps[$module][$name]; $condition->placeholder = ''; if(isset($param['class']) && in_array('date', explode(' ', $param['class']))) @@ -38,6 +39,7 @@ foreach($fieldParams as $name => $param) } } + $conditions[] = $condition; } @@ -94,6 +96,7 @@ $setting->actionURL = $actionURL; $setting->operators = $operators; $setting->conditions = $conditions; $setting->defaultData = $defaultData; +$setting->groupItemCount = $config->search->groupItems; $setting->groupTitles = array($lang->search->group1, $lang->search->group2); $setting->andText = $lang->search->andor['and']; $setting->orText = $lang->search->andor['or']; diff --git a/module/serverroom/model.php b/module/serverroom/model.php index d53f2a9dfa..2ba954a4e2 100644 --- a/module/serverroom/model.php +++ b/module/serverroom/model.php @@ -1,7 +1,7 @@ story->exportFields = ' openedBy, openedDate, assignedTo, assignedDate, mailto, reviewedBy, reviewedDate, closedBy, closedDate, closedReason, - lastEditedBy, lastEditedDate, + lastEditedBy, lastEditedDate, feedbackBy, duplicateStory, files'; $config->story->list->customCreateFields = ''; @@ -148,7 +148,6 @@ $config->story->actionList['edit']['text'] = $lang->story->edit; $config->story->actionList['edit']['hint'] = $lang->story->edit; $config->story->actionList['edit']['url'] = array('module' => 'story', 'method' => 'edit', 'params' => 'storyID={id}&kanbanGroup=default&storyType={type}'); $config->story->actionList['edit']['data-app'] = $app->tab; -$config->story->actionList['edit']['notInModal'] = true; $config->story->actionList['copy']['icon'] = 'copy'; $config->story->actionList['copy']['text'] = $lang->story->copy; diff --git a/module/story/config/dtable.php b/module/story/config/dtable.php index efe4438fd5..8ca8e9526d 100644 --- a/module/story/config/dtable.php +++ b/module/story/config/dtable.php @@ -264,7 +264,6 @@ $config->story->dtable->fieldList['sourceNote']['group'] = 10; $config->story->dtable->fieldList['feedbackBy']['name'] = 'feedbackBy'; $config->story->dtable->fieldList['feedbackBy']['title'] = $lang->story->feedbackBy; -$config->story->dtable->fieldList['feedbackBy']['type'] = 'user'; $config->story->dtable->fieldList['feedbackBy']['sortType'] = true; $config->story->dtable->fieldList['feedbackBy']['group'] = 10; diff --git a/module/story/control.php b/module/story/control.php index 1409203840..495d24496a 100755 --- a/module/story/control.php +++ b/module/story/control.php @@ -142,7 +142,7 @@ class story extends control $stories = $this->storyZen->buildStoriesForBatchCreate($productID, $storyType); if(empty($stories)) return $this->sendError($this->lang->story->errorEmptyStory, true); - if(dao::isError()) return $this->sendError(dao::getError(), true); + if(dao::isError()) return $this->sendError(dao::getError()); $storyIdList = $this->story->batchCreate($stories); if(dao::isError()) return $this->sendError(dao::getError(), true); @@ -459,7 +459,7 @@ class story extends control { if(!empty($_POST)) { - $postData = $this->storyZen->buildStoryForActivate(); + $postData = $this->storyZen->buildStoryForActivate($storyID); $changes = $this->story->activate($storyID, $postData); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -818,7 +818,7 @@ class story extends control { if($_POST) { - $storyData = $this->storyZen->buildStoryForSubmitReview(); + $storyData = $this->storyZen->buildStoryForSubmitReview($storyID); if(!$storyData) return $this->send(array('result' => 'fail', 'message' => dao::getError())); $changes = $this->story->submitReview($storyID, $storyData); @@ -876,7 +876,7 @@ class story extends control if(!empty($_POST)) { - $postData = form::data($this->config->story->form->close) + $postData = form::data($this->config->story->form->close, $storyID) ->stripTags($this->config->story->editor->close['id'], $this->config->allowedTags) ->removeIF($this->post->closedReason != 'duplicate', 'duplicateStory') ->get(); @@ -1294,7 +1294,7 @@ class story extends control { if(!empty($_POST)) { - $story = form::data($this->config->story->form->assignTo)->get(); + $story = form::data($this->config->story->form->assignTo, $storyID)->get(); $this->story->assign($storyID, $story); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -1883,7 +1883,7 @@ class story extends control /* Create field lists. */ if(!$productID or $browseType == 'bysearch') { - $this->config->story->dtable->fieldList['branch']['dataSource'] = array('module' => 'branch', 'method' => 'getAllPairs', 'params' => 1); + $this->config->story->dtable->fieldList['branch']['dataSource'] = array('module' => 'branch', 'method' => 'getAllPairs'); $this->config->story->dtable->fieldList['module']['dataSource']['method'] = 'getAllModulePairs'; $this->config->story->dtable->fieldList['module']['dataSource']['params'] = 'story'; @@ -1902,9 +1902,6 @@ class story extends control $this->story->replaceURLang($storyType); - $fileName = $this->lang->SRCommon; - if($storyType == 'requirement') $fileName = $this->lang->URCommon; - if($storyType == 'epic') $fileName = $this->lang->ERCommon; $project = null; $hasBranch = false; if($executionID) @@ -1941,7 +1938,7 @@ class story extends control $browseType = isset($this->lang->product->moreSelects[$browseType]) ? $this->lang->product->moreSelects[$browseType] : ''; } - $fileName = $productName . $this->lang->dash . $browseType . $fileName; + $fileName = $productName . $this->lang->dash . $browseType . $this->lang->common->story; } /* Unset branch field. */ diff --git a/module/story/js/batchcreate.ui.js b/module/story/js/batchcreate.ui.js index 2a7c630840..3269ab39f1 100644 --- a/module/story/js/batchcreate.ui.js +++ b/module/story/js/batchcreate.ui.js @@ -20,7 +20,7 @@ window.setModuleAndPlanByBranch = function(e) if(!$row.find('td[data-name="module"][data-ditto="on"]').length) break; } - var planLink = $.createLink('productPlan', 'ajaxGetProductPlans', 'productID=' + productID + '&branch=' + branchID); + let planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branchID + '¶ms=unexpired,noclosed&skipParent=true'); let $rows = $branch.closest('tr'); while($rows.length) { diff --git a/module/story/js/batchedit.ui.js b/module/story/js/batchedit.ui.js index 8871e7aa56..a7dc8d8387 100644 --- a/module/story/js/batchedit.ui.js +++ b/module/story/js/batchedit.ui.js @@ -86,7 +86,8 @@ window.renderRowData = function($row, index, story) }) } - $title.attr('disabled', 'disabled').attr('title', story.title).after(""); + $title.attr('title', story.title).before(""); + if(story.rawStatus != 'draft') $title.attr('disabled', 'disabled'); $row.find('.form-control-static[data-name="status"]').addClass('status-' + story.rawStatus); if($branch.length > 0) { @@ -199,7 +200,7 @@ window.renderRowData = function($row, index, story) window.loadBranches = function(product, obj) { $this = $(obj.target); - branch = $this.find('input[name^=branch]').val(); + branch = $this.find('input[name^=branch]').val().replace('branch', ''); let storyID = $this.closest('tr').find('.form-batch-input[data-name="storyIdList"]').val(); let $module = $this.closest('tr').find('.form-batch-control[data-name="module"]'); @@ -209,21 +210,33 @@ window.loadBranches = function(product, obj) { let $picker = $this.closest('tr').find('.picker-box[data-name="module"]').zui('picker'); let options = $picker.options; - options.items = items; - $picker.render(options); - $picker.$.setValue(0); + + let oldItems = options.items.map(item => item.value.toString()); + let newItems = items.map(item => item.value.toString()); + + if(JSON.stringify(newItems.slice().sort()) != JSON.stringify(oldItems.slice().sort())) + { + options.items = items; + $picker.render(options); + $picker.$.setValue(0); + } }); - let $plan = $this.closest('tr').find('.form-batch-control[data-name="plan"]'); - let planID = $plan.val(); - let planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + product + '&branch=' + branch + '&planID=' + planID + '&fieldID=' + storyID + '&needCreate=false&expired=¶m=skipParent'); + let planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + product + '&branch=' + branch + '¶ms=&skipparent=true'); $.getJSON(planLink, function(items) { let $picker = $this.closest('tr').find('.picker-box[data-name="plan"]').zui('picker'); let options = $picker.options; - options.items = items; - $picker.render(options); - $picker.$.setValue(''); + + let oldItems = options.items.map(item => item.value.toString()); + let newItems = items.map(item => item.value.toString()); + + if(JSON.stringify(newItems.slice().sort()) != JSON.stringify(oldItems.slice().sort())) + { + options.items = items; + $picker.render(options); + $picker.$.setValue(''); + } }); } diff --git a/module/story/js/common.ui.js b/module/story/js/common.ui.js index 300d422580..856aa0675e 100644 --- a/module/story/js/common.ui.js +++ b/module/story/js/common.ui.js @@ -66,24 +66,20 @@ window.loadProductPlans = function(productID, branch) if(typeof(branch) == 'undefined') branch = 0; if(!branch) branch = 0; - let planID = $('[name=plan]').val(); - let expired = config.currentMethod == 'create' ? 'unexpired' : ''; - let planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '&planID=' + planID + '&fieldID=&needCreate=true&expired='+ expired +'¶m=skipParent,forStory,' + config.currentMethod); + let params = config.currentMethod == 'create' ? 'unexpired,noclosed' : ''; + let planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '¶ms=' + params + '&skipParent=true'); let $planIdBox = $('#planIdBox'); $.get(planLink, function(data) { - if(data && data != '[]') + let items = JSON.parse(data); + let $inputGroup = $planIdBox.closest('.input-group'); + $inputGroup.html("
") + new zui.Picker('#planIdBox #plan', {items: items, name: 'plan', defaultValue: ''}); + if(items.length == 0) { - let items = JSON.parse(data); - let $inputGroup = $planIdBox.closest('.input-group'); - $inputGroup.html("
") - new zui.Picker('#planIdBox #plan', {items: items, name: 'plan', defaultValue: planID.toString()}); - if(items.length == 0) - { - $inputGroup.append(''); - $inputGroup.append(''); - } + $inputGroup.append(''); + $inputGroup.append(''); } }) }; diff --git a/module/story/js/create.ui.js b/module/story/js/create.ui.js index d605502010..a4891abee9 100644 --- a/module/story/js/create.ui.js +++ b/module/story/js/create.ui.js @@ -42,27 +42,6 @@ window.loadProduct = function(e) if(!inModal) loadPage($.createLink('story', 'create', 'productID=' + productID + '&' + createParams)); }; -window.loadProductPlans = function(productID, branch) -{ - if(typeof(branch) == 'undefined') branch = 0; - if(!branch) branch = 0; - - let planID = $('[name=plan]').val(); - let planLink = $.createLink('product', 'ajaxGetPlans', 'productID=' + productID + '&branch=' + branch + '&planID=' + planID + '&fieldID=&needCreate=true&expired=unexpired¶m=skipParent,forStory,' + config.currentMethod); - let $planIdBox = $('#planIdBox'); - - $.getJSON(planLink, function(items) - { - if(items.length == 0) return; - - const $planPicker = $('#planIdBox [name^=plan]').zui('picker'); - $planPicker.render({items: items}); - $planPicker.$.setValue(planID); - - $('#planIdBox + .btn.square, #loadProductPlans').addClass('hidden'); - }) -}; - window.setLane = function(e) { const regionID = $(e.target).val(); diff --git a/module/story/js/edit.ui.js b/module/story/js/edit.ui.js index 7f4e50bc14..259e21698c 100644 --- a/module/story/js/edit.ui.js +++ b/module/story/js/edit.ui.js @@ -68,6 +68,18 @@ window.changeReviewer = function() { var $reviewer = $('[name^="reviewer"]'); var reviewerCount = $reviewer.val().filter(Boolean).length; + var $value = $reviewer.val(); + + const filteredArray = reviewedBy.filter(value => value !== ''); + const isContained = filteredArray.every(element => $value.includes(element)); + + if(!isContained) + { + zui.Modal.alert(notDeleted); + $reviewer.zui('picker').$.setValue($value.concat(reviewedBy)); + return; + } + if(storyStatus == 'reviewing') { if(!reviewerCount) diff --git a/module/story/lang/de.php b/module/story/lang/de.php index 2313095956..07ff8eed7a 100644 --- a/module/story/lang/de.php +++ b/module/story/lang/de.php @@ -516,6 +516,7 @@ $lang->story->notice = new stdClass(); $lang->story->notice->closed = "Die ausgewählten {$lang->SRCommon} wurden bereits geschlossen!"; $lang->story->notice->reviewerNotEmpty = "This {$lang->SRCommon} needs to be reviewed, and the reviewedby is required."; $lang->story->notice->changePlan = 'The plan can be changed to only one item.'; +$lang->story->notice->notDeleted = 'People who have been reviewed cannot be deleted.'; $lang->story->convertToTask = new stdClass(); $lang->story->convertToTask->fieldList = array(); @@ -580,3 +581,6 @@ $lang->story->trackOrderByList['category'] = 'Order by category'; $lang->story->trackSortList['asc'] = ' Ascending'; $lang->story->trackSortList['desc'] = ' Descending'; + +$lang->story->error = new stdclass(); +$lang->story->error->length = "Length exceeds %d characters, cannot save. Please modify it and try again."; diff --git a/module/story/lang/en.php b/module/story/lang/en.php index 9f0bc3e555..f37c01b86b 100644 --- a/module/story/lang/en.php +++ b/module/story/lang/en.php @@ -516,6 +516,7 @@ $lang->story->notice = new stdClass(); $lang->story->notice->closed = "{$lang->SRCommon} that you select is closed!"; $lang->story->notice->reviewerNotEmpty = "This {$lang->SRCommon} needs to be reviewed, and the reviewedby is required."; $lang->story->notice->changePlan = 'The plan can be changed to only one item.'; +$lang->story->notice->notDeleted = 'People who have been reviewed cannot be deleted.'; $lang->story->convertToTask = new stdClass(); $lang->story->convertToTask->fieldList = array(); @@ -580,3 +581,6 @@ $lang->story->trackOrderByList['category'] = 'Order by category'; $lang->story->trackSortList['asc'] = ' Ascending'; $lang->story->trackSortList['desc'] = ' Descending'; + +$lang->story->error = new stdclass(); +$lang->story->error->length = "Length exceeds %d characters, cannot be saved. Please modify it and try again."; diff --git a/module/story/lang/fr.php b/module/story/lang/fr.php index 86348b1a05..340b443558 100644 --- a/module/story/lang/fr.php +++ b/module/story/lang/fr.php @@ -516,6 +516,7 @@ $lang->story->notice = new stdClass(); $lang->story->notice->closed = "La {$lang->SRCommon} que vous avez sélectionnée est malheureusement fermée !"; $lang->story->notice->reviewerNotEmpty = "This {$lang->SRCommon} needs to be reviewed, and the reviewedby is required."; $lang->story->notice->changePlan = 'The plan can be changed to only one item.'; +$lang->story->notice->notDeleted = 'People who have been reviewed cannot be deleted.'; $lang->story->convertToTask = new stdClass(); $lang->story->convertToTask->fieldList = array(); @@ -580,3 +581,6 @@ $lang->story->trackOrderByList['category'] = 'Order by category'; $lang->story->trackSortList['asc'] = ' Ascending'; $lang->story->trackSortList['desc'] = ' Descending'; + +$lang->story->error = new stdclass(); +$lang->story->error->length = "Length exceeds %d characters, cannot save. Please modify it and try again."; diff --git a/module/story/lang/zh-cn.php b/module/story/lang/zh-cn.php index fc1b7ec26a..5256b69612 100644 --- a/module/story/lang/zh-cn.php +++ b/module/story/lang/zh-cn.php @@ -516,6 +516,7 @@ $lang->story->notice = new stdClass(); $lang->story->notice->closed = "您选择的{$lang->SRCommon}已经被关闭了!"; $lang->story->notice->reviewerNotEmpty = "该{$lang->SRCommon}需要评审,评审人员不能为空。"; $lang->story->notice->changePlan = '所属计划只能改为一条,修改后才能保存成功。'; +$lang->story->notice->notDeleted = '已经评审过的人员不能删除。'; $lang->story->convertToTask = new stdClass(); $lang->story->convertToTask->fieldList = array(); @@ -580,3 +581,6 @@ $lang->story->trackOrderByList['category'] = '按类别'; $lang->story->trackSortList['asc'] = '升序'; $lang->story->trackSortList['desc'] = '降序'; + +$lang->story->error = new stdclass(); +$lang->story->error->length = "长度超过了%d个字符,无法保存,请修改后再试"; diff --git a/module/story/model.php b/module/story/model.php index da3e01d547..847df3d366 100644 --- a/module/story/model.php +++ b/module/story/model.php @@ -843,7 +843,7 @@ class storyModel extends model $this->storyTao->doUpdateSpec($storyID, $story, $oldStory, $addedFiles); $this->storyTao->doUpdateLinkStories($storyID, $story, $oldStory); - if($story->product != $oldStory->product) + if($story->product != $oldStory->product || $story->branch != $oldStory->branch) { $this->dao->update(TABLE_PROJECTSTORY)->set('product')->eq($story->product)->where('story')->eq($storyID)->exec(); $childStories = $this->getAllChildId($storyID, false); @@ -891,12 +891,27 @@ class storyModel extends model unset($oldStory->parent, $story->parent); if($this->config->edition != 'open' && $oldStory->feedback) $this->loadModel('feedback')->updateStatus('story', $oldStory->feedback, $story->status, $oldStory->status); + if(isset($story->reviewer)) + { + $oldReviewer = $this->getReviewerPairs($storyID, $oldStory->version); + $oldStory->reviewers = implode(',', array_keys($oldReviewer)); + $story->reviewers = implode(',', $story->reviewer); + if($story->reviewers != $oldStory->reviewers) + { + $oldStatus = $story->status; + $this->doUpdateReviewer($storyID, $story); + if($story->status != $oldStatus) $this->dao->update(TABLE_STORY)->set('status')->eq($story->status)->where('id')->eq($storyID)->exec(); + if($story->status == 'active') $story->finalResult = $story->status; + } + } + $changes = common::createChanges($oldStory, $story); if(!empty($comment) or !empty($changes)) { $action = !empty($changes) ? 'Edited' : 'Commented'; $actionID = $this->action->create('story', $storyID, $action, $comment); $this->action->logHistory($actionID, $changes); + if(isset($story->finalResult)) $this->action->create('story', $storyID, 'ReviewPassed', '', "pass|$oldStatus"); } if(isset($story->closedReason) and $story->closedReason == 'done') $this->loadModel('score')->create('story', 'close'); @@ -922,8 +937,9 @@ class storyModel extends model $childPath = strstr($childPath, ",{$parent->id},"); $this->dao->update(TABLE_STORY) ->set('product')->eq($parent->product) + ->set('branch')->eq($parent->branch) ->set('module')->eq(0) - ->set('root')->eq($parent->root) + ->set('root')->eq($parent->id) ->set('path')->eq($childPath) ->where('id')->eq($storyID) ->exec(); @@ -2235,6 +2251,8 @@ class storyModel extends model ->andWhere("FIND_IN_SET('{$this->config->vision}', vision)") ->beginIF($type != 'all')->andWhere('type')->in($type)->fi() ->beginIF($showGrades)->andWhere('grade')->in($showGrades)->fi() + ->beginIF(empty($this->config->enableER))->andWhere('type')->ne('epic')->fi() + ->beginIF(empty($this->config->URAndSR) && $this->config->edition != 'ipd')->andWhere('type')->ne('requirement')->fi() ->orderBy($orderBy) ->page($pager) ->fetchAll('id'); @@ -2741,6 +2759,8 @@ class storyModel extends model ->andWhere("FIND_IN_SET('{$this->config->vision}', t1.vision)") ->beginIF($type != 'all')->andWhere('t1.type')->in($type)->fi() ->beginIF($showGrades)->andWhere('t1.grade')->in($showGrades)->fi() + ->beginIF(empty($this->config->enableER))->andWhere('t1.type')->ne('epic')->fi() + ->beginIF(empty($this->config->URAndSR) && $this->config->edition != 'ipd')->andWhere('t1.type')->ne('requirement')->fi() ->orderBy($orderBy) ->page($pager, 't1.id') ->fetchAll('id'); @@ -4527,6 +4547,7 @@ class storyModel extends model $reviewerList = $this->getReviewerPairs($storyID, (int)$oldStory->version); $reviewedBy = explode(',', trim($story->reviewedBy, ',')); + if(!array_diff(array_keys($reviewerList), $reviewedBy)) { $reviewResult = $this->getReviewResult($reviewerList); @@ -5123,7 +5144,7 @@ class storyModel extends model $storyTitle = is_string($stories[$story->id]) ? $stories[$story->id] : $story->title; if(isset($gradePairs[$story->grade])) { - $options[] = array('text' => array('html' => "{$gradePairs[$story->grade]->name} {$storyTitle}"), 'value' => $story->id, 'keys' => $storyTitle); + $options[] = array('text' => array('html' => "{$gradePairs[$story->grade]->name} {$storyTitle}"), 'hint' => $storyTitle, 'value' => $story->id, 'keys' => $storyTitle); } else { diff --git a/module/story/tao.php b/module/story/tao.php index 8342a888a7..dd78439253 100644 --- a/module/story/tao.php +++ b/module/story/tao.php @@ -2503,7 +2503,7 @@ class storyTao extends storyModel */ public function reorderStories(array $stories): array { - $tree = $this->buildStoryTree($stories); + $tree = $this->buildStoryTree($stories, 0, $stories); $result = array(); $this->buildReorderResult($tree, $result); @@ -2518,16 +2518,17 @@ class storyTao extends storyModel * * @param array $stories * @param int $parentId + * @param array $originStories * @return array */ - public function buildStoryTree(array &$stories, int $parentId = 0) + public function buildStoryTree(array &$stories, int $parentId = 0, array $originStories = array()) { $tree = array(); foreach($stories as $id => $parent) { - if($parent == $parentId) + if($parent == $parentId || (!isset($originStories[$parent]) && $parentId == 0)) { - $tree[$id] = $this->buildStoryTree($stories, $id); + $tree[$id] = $this->buildStoryTree($stories, $id, $originStories); } } diff --git a/module/story/test/lib/closestory.ui.class.php b/module/story/test/lib/closestory.ui.class.php new file mode 100644 index 0000000000..541fdf2e27 --- /dev/null +++ b/module/story/test/lib/closestory.ui.class.php @@ -0,0 +1,40 @@ +openURL('story', 'view', array('id' => $storyID), 'appIframe-product'); //进入研发需求详情页 + $form = $this->loadPage('story', 'view'); + $form->dom->btn($this->lang->story->close)->click(); //点击关闭需求按钮 + + $form->wait(1); + + $form->dom->closedReason->picker($closeReason); //选择关闭原因 + $form->dom->getElement("//*[@id='zin_story_close_{$storyID}_form']/div[4]/div/button")->click(); + $form->wait(1); + + $viewPage = $this->loadPage('story', 'view'); + if($viewPage->dom->status->getText() != '已关闭') return $this->failed('需求状态不正确'); + $viewPage->dom->btn($this->lang->story->legendLifeTime)->click(); + if($viewPage->dom->closeReason->getText() != $closeReason) return $this->failed('需求关闭原因不正确'); + + return $this->success('关闭需求成功'); + } +} diff --git a/module/story/test/lib/editstory.ui.class.php b/module/story/test/lib/editstory.ui.class.php index 5011d11961..9d268f4449 100755 --- a/module/story/test/lib/editstory.ui.class.php +++ b/module/story/test/lib/editstory.ui.class.php @@ -22,7 +22,7 @@ class editStoryTester extends tester public function editStory($storyFrom) { $editStoryParam = array( - 'storyID' => '39', + 'storyID' => '2', 'kanbanGroup' => 'default', 'storyType' => 'story' ); @@ -33,7 +33,7 @@ class editStoryTester extends tester $form->dom->btn($this->lang->save)->click(); $form->wait(1); - $browsePage = $this->loadPage('story', 'view', '39'); + $browsePage = $this->loadPage('story', 'view', '2'); $viewPafe = $this->loadPage('story', 'view'); if($viewPafe->dom->storyFrom->getText() != '客户') return $this->failed('需求来源不正确'); diff --git a/module/story/test/ui/closestory.php b/module/story/test/ui/closestory.php new file mode 100644 index 0000000000..84a5b6bad2 --- /dev/null +++ b/module/story/test/ui/closestory.php @@ -0,0 +1,25 @@ +#!/usr/bin/env php +login(); + +$closeReason = array('已完成', '不做'); + +r($tester->closeStory(3, $closeReason[0])) && p('message,status') && e('关闭需求成功,SUCCESS'); diff --git a/module/story/test/ui/page/view.php b/module/story/test/ui/page/view.php index 106378e9da..08fc1f5378 100755 --- a/module/story/test/ui/page/view.php +++ b/module/story/test/ui/page/view.php @@ -9,7 +9,8 @@ class viewPage extends page 'storyName' => "//*[@id='mainContent']/div/div[1]/div[1]/span[2]", 'status' => "/html/body/div[1]/div/div/div/div[2]/div[2]/div[1]/div[2]/div[1]/div/div[6]/div[2]/span", 'storyFrom' => "//*[@id='sourceBox']", - 'historyOpenedBy' => "/html/body/div[1]/div/div/div/div[2]/div[1]/div[2]/div/div[2]/ul/li/div/div[2]/div/div/strong" + 'historyOpenedBy' => "/html/body/div[1]/div/div/div/div[2]/div[1]/div[2]/div/div[2]/ul/li/div/div[2]/div/div/strong", + 'closeReason' => "/html/body/div[1]/div/div/div/div[2]/div[2]/div[1]/div[2]/div[2]/div/div[6]/div[2]" ); $this->dom->xpath = array_merge($this->dom->xpath, $xpath); } diff --git a/module/story/ui/create.field.php b/module/story/ui/create.field.php index 13dd96becb..7432b2e984 100644 --- a/module/story/ui/create.field.php +++ b/module/story/ui/create.field.php @@ -45,6 +45,7 @@ if(isset($createFields['branch']) && $type == 'story') } $fields->field('parent') + ->id('parentBox') ->hidden(data('hiddenParent')) ->items($createFields['parent']['options']) ->value($createFields['parent']['default']); @@ -132,7 +133,7 @@ if(!(isset($createFields['branch']) && $type == 'story') && isset($createFields[ ->control('inputGroup') ->items(false) ->itemBegin('plan')->control('picker')->id('planIdBox')->items($createFields['plan']['options'])->value($createFields['plan']['default'])->multiple($type != 'story')->itemEnd() - ->item(empty($createFields['plan']['options']) ? field()->control('btn')->icon('plus')->url(createLink('productplan', 'create', 'productID=' . data('productID') . '&branch=' . data('branch')))->set(array('data-toggle' => 'modal', 'data-size' => 'lg'))->set('title', $lang->productplan->create) : null) + ->item(empty($createFields['plan']['options']) && hasPriv('productplan', 'create') ? field()->control('btn')->icon('plus')->url(createLink('productplan', 'create', 'productID=' . data('productID') . '&branch=' . data('branch')))->set(array('data-toggle' => 'modal', 'data-size' => 'lg'))->set('title', $lang->productplan->create) : null) ->item(empty($createFields['plan']['options']) ? field()->control('btn')->icon('refresh')->id("loadProductPlans")->set('title', $lang->refresh) : null); } @@ -151,12 +152,14 @@ $fields->field('sourceNote') $fields->field('feedbackBy') ->foldable() ->className('feedbackBox') + ->className(!in_array($createFields['source']['default'], $config->story->feedbackSource) ? 'hidden' : '') ->id('feedbackBy') ->value($createFields['feedbackBy']['default']); $fields->field('notifyEmail') ->foldable() ->className('feedbackBox') + ->className(!in_array($createFields['source']['default'], $config->story->feedbackSource) ? 'hidden' : '') ->id('notifyEmail') ->value($createFields['notifyEmail']['default']); diff --git a/module/story/ui/edit.html.php b/module/story/ui/edit.html.php index 92b99fd13e..8c6468308c 100644 --- a/module/story/ui/edit.html.php +++ b/module/story/ui/edit.html.php @@ -48,8 +48,11 @@ jsVar('oldProductID', $story->product); jsVar('oldGrade', $story->grade); jsVar('oldParent', $story->parent); jsVar('lastReviewer', explode(',', $lastReviewer)); +jsVar('reviewedBy', explode(',', trim($story->reviewedBy, ','))); +jsVar('defaultReviewer', array_keys($fields['reviewer']['options'])); jsVar('storyReviewers', $storyReviewers); jsVar('reviewerNotEmpty', $lang->story->notice->reviewerNotEmpty); +jsVar('notDeleted', $lang->story->notice->notDeleted); jsVar('twins', $story->twins); jsVar('relievedTwinsTip', $lang->story->relievedTwinsTip); jsVar('changeProductTips', $lang->story->changeProductTips); diff --git a/module/story/ui/view.html.php b/module/story/ui/view.html.php index 6656859586..010bdc8ab4 100644 --- a/module/story/ui/view.html.php +++ b/module/story/ui/view.html.php @@ -197,7 +197,7 @@ foreach($actions as $key => $action) continue; } - if(isset($action['key']) && $action['key'] == 'createTask' && ($story->type != 'story' || $story->isParent == '1' || $app->tab == 'product' || $isInModal || ($app->tab == 'project' && !empty($project) && $project->multiple))) + if(isset($action['key']) && $action['key'] == 'createTask' && ($story->type != 'story' || in_array($story->status, array('reviewing', 'closed')) || $story->isParent == '1' || $app->tab == 'product' || $isInModal || ($app->tab == 'project' && !empty($project) && $project->multiple))) { unset($actions[$key]); continue; diff --git a/module/story/zen.php b/module/story/zen.php index 54568e7d67..ef19bfe470 100644 --- a/module/story/zen.php +++ b/module/story/zen.php @@ -1244,7 +1244,7 @@ class storyZen extends story $fields = $this->config->story->form->edit; $editorFields = array_keys(array_filter(array_map(function($config){return $config['control'] == 'editor';}, $fields))); - $storyData = form::data($fields) + $storyData = form::data($fields, $storyID) ->add('lastEditedBy', $this->app->user->account) ->add('lastEditedDate', $now) ->add('demand', $oldStory->demand) @@ -1307,7 +1307,7 @@ class storyZen extends story $now = helper::now(); $fields = $this->config->story->form->change; $editorFields = array_keys(array_filter(array_map(function($config){return $config['control'] == 'editor';}, $fields))); - $story = form::data($fields) + $story = form::data($fields, $storyID) ->setDefault('lastEditedBy', $this->app->user->account) ->setDefault('deleteFiles', array()) ->setDefault('lastEditedDate', $now) @@ -1420,7 +1420,6 @@ class storyZen extends story if(isset($_POST['plan']) and is_array($_POST['plan'])) $story->plan = trim(implode(',', $_POST['plan']), ','); if(isset($_POST['branch']) and $_POST['branch'] == 0) $story->branch = 0; if(isset($story->stage) and $oldStory->stage != $story->stage) $story->stagedBy = (strpos('tested|verified|rejected|pending|released|closed', $story->stage) !== false) ? $this->app->user->account : ''; - if(isset($_POST['reviewer']) or isset($_POST['needNotReview'])) $this->story->doUpdateReviewer($storyID, $story); } /** @@ -1459,7 +1458,7 @@ class storyZen extends story $editorFields = array_keys(array_filter(array_map(function($config){return $config['control'] == 'editor';}, $fields))); $result = $this->post->result; $closedReason = $this->post->closedReason; - $storyData = form::data($fields) + $storyData = form::data($fields, $storyID) ->setDefault('lastEditedBy', $this->app->user->account) ->setDefault('lastEditedDate', $now) ->removeIF($result != 'reject', 'closedReason,duplicateStory') @@ -1497,6 +1496,11 @@ class storyZen extends story $stories = form::batchData($fields)->get(); foreach($stories as $i => $story) { + if(!empty($story->title) && mb_strlen($story->title) > 255) + { + dao::$errors["title[$i]"] = sprintf($this->lang->story->error->length, 255); + } + $story->type = $storyType; $story->status = (empty($story->reviewer) && !$forceReview) ? 'active' : 'reviewing'; $story->status = $saveDraft ? 'draft' : $story->status; @@ -1544,6 +1548,7 @@ class storyZen extends story $story->stage = empty($story->stage) ? $oldStory->stage : $story->stage; $story->branch = $story->branch === '' ? $oldStory->branch : str_replace('branch', '', $story->branch); + if(empty($story->roadmap)) $story->roadmap = $oldStory->roadmap; if($oldStory->assignedTo != $story->assignedTo) $story->assignedDate = $now; if($oldStory->parent < 0) $story->plan = ''; if($story->stage != $oldStory->stage) $story->stagedBy = (str_contains('|tested|verified|rejected|pending|released|closed|', "|{$story->stage}|")) ? $account : ''; @@ -1910,11 +1915,13 @@ class storyZen extends story /** * Build story post data for activating the story. * + * @param int $storyID + * @access protected * @return object */ - protected function buildStoryForActivate(): object + protected function buildStoryForActivate(int $storyID): object { - $postData = form::data($this->config->story->form->activate)->get(); + $postData = form::data($this->config->story->form->activate, $storyID)->get(); $story = $this->loadModel('file')->processImgURL($postData, $this->config->story->editor->activate['id'], $this->post->uid); return $story; } @@ -1922,9 +1929,11 @@ class storyZen extends story /** * Build story post data for submitReview the story. * + * @param int $storyID + * @access protected * @return object|false */ - protected function buildStoryForSubmitReview(): object|false + protected function buildStoryForSubmitReview(int $storyID): object|false { if(isset($_POST['reviewer'])) $_POST['reviewer'] = array_filter($_POST['reviewer']); if(!$this->post->needNotReview and empty($_POST['reviewer'])) @@ -1933,7 +1942,7 @@ class storyZen extends story return false; } - return form::data($this->config->story->form->submitReview)->get(); + return form::data($this->config->story->form->submitReview, $storyID)->get(); } /** diff --git a/module/task/config/form.php b/module/task/config/form.php index 80db50adf9..73c8f4f1b1 100644 --- a/module/task/config/form.php +++ b/module/task/config/form.php @@ -67,6 +67,7 @@ $config->task->form->edit['type'] = array('type' => 'string', 'requi $config->task->form->edit['status'] = array('type' => 'string', 'required' => true); $config->task->form->edit['pri'] = array('type' => 'int', 'required' => false, 'default' => 0); $config->task->form->edit['estStarted'] = array('type' => 'date', 'required' => false, 'default' => null); +$config->task->form->edit['realStarted'] = array('type' => 'datetime', 'required' => false, 'default' => null); $config->task->form->edit['deadline'] = array('type' => 'date', 'required' => false, 'default' => null); $config->task->form->edit['estimate'] = array('type' => 'float', 'required' => false, 'default' => 0); $config->task->form->edit['left'] = array('type' => 'float', 'required' => false, 'default' => 0); diff --git a/module/task/control.php b/module/task/control.php index 23330e5bac..1263dd22dc 100755 --- a/module/task/control.php +++ b/module/task/control.php @@ -258,9 +258,7 @@ class task extends control if(!empty($_POST)) { - $task = form::data($this->config->task->form->assign) - ->add('id', $taskID) - ->get(); + $task = form::data($this->config->task->form->assign, $taskID)->add('id', $taskID)->get(); $this->task->assign($task); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); @@ -665,8 +663,7 @@ class task extends control $oldTask = $this->task->getByID($taskID); /* Init task data. */ - $task = form::data($this->config->task->form->pause)->get(); - $task->id = $taskID; + $task = form::data($this->config->task->form->pause, $taskID)->add('id', $taskID)->get(); /* Pause task. */ $changes = $this->task->pause($task, $output); diff --git a/module/task/css/recordworkhour.ui.css b/module/task/css/recordworkhour.ui.css index 0b7918afa5..619ca5e187 100644 --- a/module/task/css/recordworkhour.ui.css +++ b/module/task/css/recordworkhour.ui.css @@ -7,3 +7,8 @@ .modal-body .panel-form-batch {padding-top: 1rem;} .modal-body .table {margin-bottom: 1.5rem;} .modal-body .table + .panel-form-batch {padding-top: 0;} + +#toggleFoldIcon:hover {cursor: pointer;} +#toggleFoldIcon {margin-bottom: 10px;} +#toggleFoldIcon .angle-top > .icon-back-circle:before {transform: rotate(90deg);} +#toggleFoldIcon .angle-down > .icon-back-circle:before {transform: rotate(270deg);} diff --git a/module/task/js/recordworkhour.ui.js b/module/task/js/recordworkhour.ui.js index 3f9138c3ff..84880307bd 100644 --- a/module/task/js/recordworkhour.ui.js +++ b/module/task/js/recordworkhour.ui.js @@ -24,3 +24,30 @@ $(document).on('click', '.modal-actions > button', function() { loadCurrentPage(); }) + +window.toggleFold = function() +{ + const fold = $('#toggleFoldIcon').find('.angle-down').length > 0; + const $icon = $('#toggleFoldIcon').find('.icon-toggle'); + const $text = $('#toggleFoldIcon').find('.text'); + + $.cookie.set('taskEffortFold', fold ? 0 : 1, {expires:config.cookieLife, path:config.webRoot}); + + if(fold) + { + /* Update icon and text. */ + $icon.removeClass('angle-down').addClass('angle-top'); + $text.text(foldEffort); + + /* Show all efforts. */ + $('.taskEffort > tbody > tr').removeClass('hidden'); + } + else + { + $icon.removeClass('angle-top').addClass('angle-down'); + $text.text(unfoldEffort); + + /* Efforts whose number is greater than 3 are hidden. */ + $('.taskEffort > tbody').children('tr:nth-child(n+5)').addClass('hidden'); + } +} diff --git a/module/task/lang/de.php b/module/task/lang/de.php index 9cd2ba8908..0fe02ae026 100644 --- a/module/task/lang/de.php +++ b/module/task/lang/de.php @@ -318,6 +318,7 @@ $lang->task->error->beginGtExecution = "The 'StartDate' of the task must be les $lang->task->error->endGtExecution = "The 'Deadline' of the task must be less than or equal the 'Planned End' of %s to %s."; $lang->task->error->endLtExecution = "The 'Deadline' of the task must be greater than or equal the 'Planned Begin' of %s to %s."; $lang->task->error->dateExceed = "Because the scheduled date of task %s exceeds the scheduled date of {$lang->execution->common}, it is automatically changed to the scheduled date of {$lang->execution->common}"; +$lang->task->error->length = "Length exceeds the limit of %d characters, cannot be saved. Please modify it again."; /* Report. */ $lang->task->report = new stdclass(); diff --git a/module/task/lang/en.php b/module/task/lang/en.php index b87062a36d..a3eff554cc 100755 --- a/module/task/lang/en.php +++ b/module/task/lang/en.php @@ -318,6 +318,7 @@ $lang->task->error->beginGtExecution = "The 'StartDate' of the task must be les $lang->task->error->endGtExecution = "The 'Deadline' of the task must be less than or equal the 'Planned End' of %s to %s."; $lang->task->error->endLtExecution = "The 'Deadline' of the task must be greater than or equal the 'Planned Begin' of %s to %s."; $lang->task->error->dateExceed = "Because the scheduled date of task %s exceeds the scheduled date of {$lang->execution->common}, it is automatically changed to the scheduled date of {$lang->execution->common}"; +$lang->task->error->length = "Length exceeds the limit of %d characters, cannot be saved. Please modify it again."; /* Report. */ $lang->task->report = new stdclass(); diff --git a/module/task/lang/fr.php b/module/task/lang/fr.php index 7ed73c0cb9..f1b02f1cb3 100644 --- a/module/task/lang/fr.php +++ b/module/task/lang/fr.php @@ -318,6 +318,7 @@ $lang->task->error->beginGtExecution = "The 'StartDate' of the task must be les $lang->task->error->endGtExecution = "The 'Deadline' of the task must be less than or equal the 'Planned End' of %s to %s."; $lang->task->error->endLtExecution = "The 'Deadline' of the task must be greater than or equal the 'Planned Begin' of %s to %s."; $lang->task->error->dateExceed = "Because the scheduled date of task %s exceeds the scheduled date of {$lang->execution->common}, it is automatically changed to the scheduled date of {$lang->execution->common}"; +$lang->task->error->length = "Length exceeds the limit of %d characters, cannot be saved. Please modify it again."; /* Report. */ $lang->task->report = new stdclass(); diff --git a/module/task/lang/zh-cn.php b/module/task/lang/zh-cn.php index 9a02254a59..ca2886344c 100755 --- a/module/task/lang/zh-cn.php +++ b/module/task/lang/zh-cn.php @@ -318,6 +318,7 @@ $lang->task->error->beginGtExecution = "任务开始日期应小于等于%s的 $lang->task->error->endGtExecution = "任务截止日期应小于等于%s的结束日期:%s。"; $lang->task->error->endLtExecution = "任务截止日期应大于等于%s的开始日期:%s。"; $lang->task->error->dateExceed = "任务%s的计划日期超过该{$lang->execution->common}计划日期,将自动改为{$lang->execution->common}的计划日期。"; +$lang->task->error->length = "长度超过了%d个字符,无法保存,请修改后再试"; /* Report. */ $lang->task->report = new stdclass(); diff --git a/module/task/model.php b/module/task/model.php index 91c7c9b1da..4e885f73e6 100755 --- a/module/task/model.php +++ b/module/task/model.php @@ -54,7 +54,7 @@ class taskModel extends model ->exec(); if(dao::isError()) return false; - if($task->consumed != $oldTask->consumed || $task->left != $oldTask->left) $this->loadModel('program')->refreshProjectStats($oldTask->project); + if($task->left != $oldTask->left) $this->loadModel('program')->refreshProjectStats($oldTask->project); if($oldTask->parent > 0) $this->updateParentStatus($taskID); if($oldTask->parent == '-1') @@ -395,6 +395,7 @@ class taskModel extends model if(!empty($task->parent)) $this->updateParent($task, $isParentChanged); if($this->config->edition != 'open' && $oldTask->feedback) $this->loadModel('feedback')->updateStatus('task', $oldTask->feedback, $task->status, $oldTask->status); + if(!empty($oldTask->mode) && empty($task->mode)) $this->dao->delete()->from(TABLE_TASKTEAM)->where('task')->eq($task->id)->exec(); } /** @@ -1881,7 +1882,6 @@ class taskModel extends model ->where('deleted')->eq(0) ->andWhere('parent')->le(0) ->andWhere('status')->notin('cancel,closed') - ->andWhere('consumed')->eq('0') ->andWhere('execution')->eq($executionID) ->beginIF($appendIdList)->orWhere('id')->in($appendIdList)->fi() ->fetchPairs(); diff --git a/module/task/ui/create.field.php b/module/task/ui/create.field.php index 2ef8dd89a4..5a2a425b1e 100644 --- a/module/task/ui/create.field.php +++ b/module/task/ui/create.field.php @@ -22,7 +22,7 @@ $fields->field('mailto')->foldable(); $fields->field('keywords')->foldable(); /* Set assignedTo field. */ -$buildAssginedTo = function($props) +$buildAssignedTo = function() { $assignedToListBox = null; if(!empty(data('task.team'))) @@ -75,7 +75,7 @@ $buildAssginedTo = function($props) $fields->field('assignedToBox') ->label($lang->task->assignedTo) ->checkbox(array('text' => $lang->task->multiple, 'name' => 'multiple', 'checked' => !empty(data('task.mode')))) - ->control($buildAssginedTo); + ->control($buildAssignedTo); /* Set name field width. */ $nameWidth = 'w-1/2'; @@ -104,7 +104,7 @@ $fields->field('lane') ->value(data('laneID')); /* Set story field control. */ -$buildStoryBox = function($props) +$buildStoryBox = function() { if(!empty(data('execution.hasProduct'))) { @@ -119,6 +119,7 @@ $buildStoryBox = function($props) setClass('text-primary'), isInModal() ? setData('toggle', 'modal') : null, isInModal() ? setData('size', 'lg') : null, + !isInModal() ? set('target', '_blank') : null, setData('app', data('app.tab')), data('lang.execution.linkStory') ) @@ -214,7 +215,7 @@ $fields->field('taskEstimate') ->control('input'); /* Set test story task control. */ -$buildTestStoryBox = function($props) +$buildTestStoryBox = function() { return div(setID('testStoryBox')); }; diff --git a/module/task/ui/linearefforts.html.php b/module/task/ui/linearefforts.html.php index 55df91a4f9..3f1ec450bd 100644 --- a/module/task/ui/linearefforts.html.php +++ b/module/task/ui/linearefforts.html.php @@ -56,10 +56,13 @@ foreach($myCountList as $order => $count) $allOrders[$order] ); + $i = 1; foreach($myEfforts[$order] as $index => $effort) { + $hidden = ($taskEffortFold and $i > 3) ? 'hidden' : ''; $myEffortTable[] = h::tr ( + setClass($hidden), $tdDom, h::td($effort->date), h::td(zget($users, $effort->account)), @@ -84,8 +87,11 @@ foreach($myCountList as $order => $count) ) ); $tdDom = null; + $i ++; } } +$iconClass = $taskEffortFold ? 'angle-down' : 'angle-top'; +$iconText = $taskEffortFold ? $lang->task->unfoldEffort : $lang->task->foldEffort; $allEffortTable = array(); foreach($recorders as $order => $accounts) @@ -125,7 +131,7 @@ div set::title($lang->task->myEffort), h::table ( - setClass('table condensed bordered'), + setClass('table condensed bordered taskEffort'), h::tr ( h::th @@ -161,7 +167,15 @@ div ) ), $myEffortTable - ) + ), + count($myEffortTable) > 3 ? div + ( + setID('toggleFoldIcon'), + on::click('toggleFold'), + setClass('text-primary'), + span(setClass($iconClass . ' mr-1 icon-toggle'), icon('back-circle')), + span(setClass('text'), $iconText) + ) : null ), tabPane ( diff --git a/module/task/ui/recordworkhour.html.php b/module/task/ui/recordworkhour.html.php index 731fd2b8a8..964d6fff44 100644 --- a/module/task/ui/recordworkhour.html.php +++ b/module/task/ui/recordworkhour.html.php @@ -11,6 +11,9 @@ declare(strict_types=1); namespace zin; jsVar('confirmRecord', $lang->task->confirmRecord); +jsVar('foldEffort', $lang->task->foldEffort); +jsVar('unfoldEffort', $lang->task->unfoldEffort); + if(isInModal()) set::id("modal-record-hours-task-{$task->id}"); modalHeader @@ -57,12 +60,15 @@ if($efforts) else { $effortRows = array(); + $i = 1; foreach($efforts as $effort) { $canOperateEffort = $this->task->canOperateEffort($task, $effort); $operateTips = $canOperateEffort ? '' : $lang->task->effortOperateTips; + $hidden = ($taskEffortFold and $i > 3) ? 'hidden' : ''; $effortRows[] = h::tr ( + setClass($hidden), h::td($effort->id), h::td($effort->date), h::td(zget($users, $effort->account)), @@ -90,6 +96,7 @@ if($efforts) ) : null ) ); + $i ++; } div ( @@ -98,7 +105,7 @@ if($efforts) ); h::table ( - setClass('table condensed bordered'), + setClass('table condensed bordered taskEffort'), h::tr ( h::th @@ -135,31 +142,47 @@ if($efforts) ), $effortRows ); + if(count($efforts) > 3) + { + $iconClass = $taskEffortFold ? 'angle-down' : 'angle-top'; + $iconText = $taskEffortFold ? $lang->task->unfoldEffort : $lang->task->foldEffort; + div + ( + setID('toggleFoldIcon'), + on::click('toggleFold'), + setClass('text-primary'), + span(setClass($iconClass . ' mr-1 icon-toggle'), icon('back-circle')), + span(setClass('text'), $iconText) + ); + } } } if(!$this->task->canOperateEffort($task)) { - $notice = ''; - if(!isset($task->members[$app->user->account])) + if($task->status != 'closed') { - $notice = html(sprintf($lang->task->deniedNotice, '' . $lang->task->teamMember . '', $lang->task->logEfforts)); - } - elseif($task->assignedTo != $app->user->account and $task->mode == 'linear') - { - $notice = html(sprintf($lang->task->deniedNotice, '' . zget($users, $task->assignedTo) . '', $lang->task->logEfforts)); - } + $notice = ''; + if(!isset($task->members[$app->user->account])) + { + $notice = html(sprintf($lang->task->deniedNotice, '' . $lang->task->teamMember . '', $lang->task->logEfforts)); + } + elseif($task->assignedTo != $app->user->account and $task->mode == 'linear') + { + $notice = html(sprintf($lang->task->deniedNotice, '' . zget($users, $task->assignedTo) . '', $lang->task->logEfforts)); + } - div - ( - setClass('alert with-icon'), - icon('exclamation-sign text-gray text-4xl'), div ( - setClass('content'), - $notice - ) - ); + setClass('alert with-icon'), + icon('exclamation-sign text-gray text-4xl'), + div + ( + setClass('content'), + $notice + ) + ); + } } else { diff --git a/module/task/ui/view.html.php b/module/task/ui/view.html.php index 78d6881af3..d869238a56 100644 --- a/module/task/ui/view.html.php +++ b/module/task/ui/view.html.php @@ -79,6 +79,7 @@ if($task->fromBug) $sections[] = setting() ->title($lang->task->fromBug) ->control('detailCard') + ->url(createLink('bug', 'view', "bugID=$fromBug->id")) ->object($fromBug) ->content(setting()->title($lang->bug->steps)->control('html')->content(empty($fromBug->steps) ? $lang->noData : $fromBug->steps)); } diff --git a/module/task/zen.php b/module/task/zen.php index 1ab54899c5..ce8950c884 100644 --- a/module/task/zen.php +++ b/module/task/zen.php @@ -407,13 +407,21 @@ class taskZen extends task if(!$orderBy) $orderBy = 'id_desc'; /* Set the fold state of the current task. */ - $taskEffortFold = 0; - $currentAccount = $this->app->user->account; - if($task->assignedTo == $currentAccount) $taskEffortFold = 1; - if(!empty($task->team)) + $referer = strtolower($_SERVER['HTTP_REFERER']); + if(strpos($referer, 'recordworkhour') and $this->cookie->taskEffortFold !== false) { - $teamMember = array_column($task->team, 'account'); - if(in_array($currentAccount, $teamMember)) $taskEffortFold = 1; + $taskEffortFold = $this->cookie->taskEffortFold; + } + else + { + $taskEffortFold = 0; + $currentAccount = $this->app->user->account; + if($task->assignedTo == $currentAccount) $taskEffortFold = 1; + if(!empty($task->team)) + { + $teamMember = array_column($task->team, 'account'); + if(in_array($currentAccount, $teamMember)) $taskEffortFold = 1; + } } $this->view->title = $this->lang->task->record; @@ -446,7 +454,7 @@ class taskZen extends task if(dao::isError()) return false; $now = helper::now(); - $task = form::data($this->config->task->form->edit) + $task = form::data($this->config->task->form->edit, $task->id) ->add('id', $task->id) ->add('lastEditedDate', $now) ->setIF(!$task->assignedTo && !empty($oldTask->team) && !empty($this->post->team), 'assignedTo', $this->task->getAssignedTo4Multi($this->post->team, $oldTask)) @@ -642,7 +650,7 @@ class taskZen extends task */ protected function buildTaskForActivate(int $taskID): object { - $task = form::data()->add('id', $taskID)->get(); + $task = form::data($this->config->task->form->activate, $taskID)->add('id', $taskID)->get(); unset($task->comment); return $this->loadModel('file')->processImgURL($task, $this->config->task->editor->activate['id'], (string)$this->post->uid); @@ -659,7 +667,7 @@ class taskZen extends task protected function buildTaskForStart(object $oldTask): false|object { $now = helper::now(); - $task = form::data($this->config->task->form->start)->add('id', $oldTask->id) + $task = form::data($this->config->task->form->start, $oldTask->id)->add('id', $oldTask->id) ->setIF($oldTask->assignedTo != $this->app->user->account, 'assignedDate', $now) ->get(); @@ -692,7 +700,7 @@ class taskZen extends task protected function buildTaskForCancel(object $oldTask): object { $now = helper::now(); - $task = form::data($this->config->task->form->cancel) + $task = form::data($this->config->task->form->cancel, $oldTask->id) ->add('id', $oldTask->id) ->setDefault('status', 'cancel') ->setDefault('assignedTo', $oldTask->openedBy) @@ -789,7 +797,7 @@ class taskZen extends task protected function buildTaskForFinish(object $oldTask): object { $now = helper::now(); - $task = form::data($this->config->task->form->finish) + $task = form::data($this->config->task->form->finish, $oldTask->id) ->setIF(!$this->post->realStarted && helper::isZeroDate($oldTask->realStarted), 'realStarted', $now) ->setDefault('assignedTo', $oldTask->openedBy) ->get(); @@ -841,7 +849,7 @@ class taskZen extends task */ protected function buildTaskForClose(object $oldTask): object { - $task = form::data($this->config->task->form->close)->add('id', $oldTask->id) + $task = form::data($this->config->task->form->close, $oldTask->id)->add('id', $oldTask->id) ->setIF($oldTask->status == 'done', 'closedReason', 'done') ->setIF($oldTask->status == 'cancel', 'closedReason', 'cancel') ->get(); @@ -953,7 +961,11 @@ class taskZen extends task foreach($tasks as $rowIndex => $task) { - if(!empty($this->post->estimate[$rowIndex]) and !preg_match("/^[0-9]+(.[0-9]+)?$/", $this->post->estimate[$rowIndex])) + if(mb_strlen($task->name) > 255) + { + dao::$errors["name[$rowIndex]"] = sprintf($this->lang->task->error->length, 255); + } + if(!empty($this->post->estimate[$rowIndex]) and !preg_match("/^[0-9]+(.[0-9]+)?$/", (string)$this->post->estimate[$rowIndex])) { dao::$errors["estimate[$rowIndex]"] = $this->lang->task->error->estimateNumber; } @@ -1684,7 +1696,7 @@ class taskZen extends task if(dao::isError()) return array('result' => 'fail', 'message' => dao::getError()); /* Return task id when call the API. */ - if($this->viewType == 'json' || (defined('RUN_MODE') && RUN_MODE == 'api')) return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'idList' => $taskIdList)); + if($this->viewType == 'json' || (defined('RUN_MODE') && RUN_MODE == 'api')) return array('result' => 'success', 'message' => $this->lang->saveSuccess, 'idList' => $taskIdList); $response['result'] = 'success'; $response['message'] = $this->lang->saveSuccess; diff --git a/module/testcase/control.php b/module/testcase/control.php index 20004d74af..5717ee70d3 100644 --- a/module/testcase/control.php +++ b/module/testcase/control.php @@ -489,7 +489,7 @@ class testcase extends control if(!empty($_POST)) { - $formData = form::data($this->config->testcase->form->edit); + $formData = form::data($this->config->testcase->form->edit, $caseID); $case = $this->testcaseZen->prepareEditExtras($formData, $oldCase); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); diff --git a/module/testcase/js/browse.ui.js b/module/testcase/js/browse.ui.js index cff888ac3a..a7f07901fd 100644 --- a/module/testcase/js/browse.ui.js +++ b/module/testcase/js/browse.ui.js @@ -153,14 +153,14 @@ window.onRenderCell = function(result, {row, col}) { result.shift(); // 移除带链接的场景名称 result.push({html: data.title}); // 添加不带链接的场景名称 - if(data.grade == 1 && module) result.unshift({html: '' + module + ''}); // 顶级场景添加模块标签 - result.unshift({html: '' + scene + ''}); // 添加场景标签 + if(data.grade == 1 && module) result.unshift({html: '' + module + ''}); // 顶级场景添加模块标签 + result.unshift({html: '' + scene + ''}); // 添加场景标签 if(!this.options.customData.isOnlyScene && data.hasCase == false) result.push({html: '(' + noCase + ')'}); // 添加暂无用例标签 } else // 用例 { - if(data.auto == 'auto') result.unshift({html: '' + automated + ''}); // 添加自动化标签 - if(module) result.unshift({html: '' + module + ''}); // 添加模块标签 + if(data.auto == 'auto') result.unshift({html: '' + automated + ''}); // 添加自动化标签 + if(module) result.unshift({html: '' + module + ''}); // 添加模块标签 } } if(col.name == 'pri' && row.data.isScene) @@ -210,14 +210,15 @@ window.setStatistics = function(element, checks) /** * Get selected case id list. * + * @param string selector * @access public * @return void */ -function getCheckedCaseIdList() +function getCheckedCaseIdList(selector) { let caseIdList = ''; - const dtable = zui.DTable.query('#table-testcase-browse'); + const dtable = zui.DTable.query('#' + selector); $.each(dtable.$.getChecks(), function(index, caseID) { if(index > 0) caseIdList += ','; diff --git a/module/testcase/js/createbug.ui.js b/module/testcase/js/createbug.ui.js index 26339c5d1a..c387b68c7d 100644 --- a/module/testcase/js/createbug.ui.js +++ b/module/testcase/js/createbug.ui.js @@ -56,7 +56,8 @@ function createBug(event) }); var link = $.createLink('bug', 'create', $form.data('params') + ',stepIdList=' + stepIdList); - openPage(link, 'qa'); + window.open(link, '_blank'); + //openPage(link, 'qa'); } /** diff --git a/module/testcase/model.php b/module/testcase/model.php index 16e2ac10f7..10d91faac6 100755 --- a/module/testcase/model.php +++ b/module/testcase/model.php @@ -63,7 +63,7 @@ class testcaseModel extends model /* Insert testcase steps. */ $this->testcaseTao->insertSteps($caseID, $case->steps, $case->expects, $case->stepType); - if($case->auto != 'auto') + if(isset($case->auto) && $case->auto != 'auto') { ob_start(); setcookie('onlyAutoCase', '0'); @@ -1518,9 +1518,9 @@ class testcaseModel extends model $trimmedStep = trim($step); if(empty($trimmedStep)) continue; - preg_match('/^((([0-9]+)[.]([0-9]+))[.]([0-9]+))[.、](.*)$/U', $trimmedStep, $out); - if(!$out) preg_match('/^(([0-9]+)[.]([0-9]+))[.、](.*)$/U', $trimmedStep, $out); - if(!$out) preg_match('/^([0-9]+)[.、](.*)$/U', $trimmedStep, $out); + preg_match('/^((([0-9]+)[.]([0-9]+))[.]([0-9]+))[.、](.*)$/Uu', $trimmedStep, $out); + if(!$out) preg_match('/^(([0-9]+)[.]([0-9]+))[.、](.*)$/Uu', $trimmedStep, $out); + if(!$out) preg_match('/^([0-9]+)[.、](.*)$/Uu', $trimmedStep, $out); if($out) { $count = count($out); @@ -1561,10 +1561,19 @@ class testcaseModel extends model } unset($num); unset($sign); + if($stepKey == 'expect' && !empty($stepData[$row]['desc'])) + { + foreach($stepData[$row]['desc'] as $stepDescValue) + { + if(empty($stepDescValue['number'])) continue; + $caseNumber = $stepDescValue['number']; + + if($stepDescValue && !isset($caseStep[$caseNumber]) || empty($caseStep[$caseNumber]['content'])) $caseStep[$caseNumber] = ''; + } + } $stepVars += count($caseStep, COUNT_RECURSIVE) - count($caseStep); $stepData[$row][$stepKey] = $caseStep; } - } } return $stepData; diff --git a/module/testcase/tao.php b/module/testcase/tao.php index 3cf88a4e35..202a36a630 100644 --- a/module/testcase/tao.php +++ b/module/testcase/tao.php @@ -63,14 +63,11 @@ class testcaseTao extends testcaseModel */ protected function getModuleProjectCases(int $productID, int|string $branch = 0, int|array $moduleIdList = 0, string $browseType = '', string $auto = 'no', string $caseType = '', string $orderBy = 'id_desc', object $pager = null): array { - $executions = $this->loadModel('execution')->getIdList((int)$this->session->project); - array_push($executions, $this->session->project); - return $this->dao->select('distinct t1.*, t2.*, t4.title AS storyTitle')->from(TABLE_PROJECTCASE)->alias('t1') ->leftJoin(TABLE_CASE)->alias('t2')->on('t1.case = t2.id') ->leftJoin(TABLE_PROJECTSTORY)->alias('t3')->on('t3.story = t2.story') ->leftJoin(TABLE_STORY)->alias('t4')->on('t3.story = t4.id') - ->where('t1.project')->in($executions) + ->where('t1.project')->eq((int)$this->session->project) ->beginIF(!empty($productID))->andWhere('t2.product')->eq($productID)->fi() ->beginIF(!empty($productID) && $branch !== 'all')->andWhere('t2.branch')->eq($branch)->fi() ->beginIF($moduleIdList)->andWhere('t2.module')->in($moduleIdList)->fi() diff --git a/module/testcase/ui/browse.html.php b/module/testcase/ui/browse.html.php index b11e35f536..1515d321ab 100644 --- a/module/testcase/ui/browse.html.php +++ b/module/testcase/ui/browse.html.php @@ -159,7 +159,7 @@ div( modal ( - on::click('button[type="submit"]', 'getCheckedCaseIdList'), + on::click('button[type="submit"]', "getCheckedCaseIdList('table-{$app->rawModule}-{$app->rawMethod}')"), setID('importToLib'), set::modalProps(array('title' => $lang->testcase->importToLib)), formPanel diff --git a/module/testcase/zen.php b/module/testcase/zen.php index ff2575704c..445904c352 100755 --- a/module/testcase/zen.php +++ b/module/testcase/zen.php @@ -1433,10 +1433,20 @@ class testcaseZen extends testcase $forceNotReview = $this->testcase->forceNotReview(); $storyVersions = array(); $testcases = form::batchData($this->config->testcase->form->batchCreate)->get(); + + $projectID = $this->app->tab == 'project' ? $this->session->project : 0; + $executionID = 0; + if($projectID) + { + $project = $this->loadModel('project')->getByID($projectID); + if(!$project->multiple) $executionID = $this->dao->select('id')->from(TABLE_PROJECT)->where('parent')->eq($projectID)->fetch('id'); + } + foreach($testcases as $testcase) { - $testcase->product = $productID; - if($this->app->tab == 'project') $testcase->project = $this->session->project; + $testcase->product = $productID; + $testcase->project = $projectID; + $testcase->execution = $executionID; $testcase->openedBy = $account; $testcase->openedDate = $now; $testcase->status = $forceNotReview || $testcase->review == 0 ? 'normal' : 'wait'; @@ -2394,7 +2404,7 @@ class testcaseZen extends testcase $now = helper::now(); $status = $this->getStatusForReview($oldCase); - $case = form::data($this->config->testcase->form->review)->add('id', $caseID) + $case = form::data($this->config->testcase->form->review, $caseID)->add('id', $caseID) ->setForce('status', $status) ->setDefault('reviewedDate', substr($now, 0, 10)) ->setDefault('lastEditedBy', $this->app->user->account) @@ -2478,12 +2488,22 @@ class testcaseZen extends testcase /* 获取产品列表。Get productList. */ if($this->app->tab == 'project' && !$productID) { - $productList = $products; + if($projectID) + { + $products = $this->loadModel('product')->getProducts($projectID); + $productList = array(0 => ''); + foreach($products as $product) $productList[$product->id] = $product->name; + $productList['all'] = $this->lang->product->allProductsOfProject; + } + else + { + $productList = $products; + } $this->config->testcase->search['params']['story']['values'] = $this->loadModel('story')->getExecutionStoryPairs($projectID, 0, 'all', $moduleID, 'full', 'active'); } else { - $productList = array(); + $productList = array(0 => ''); $productList['all'] = $this->lang->all; if(isset($products[$productID])) $productList[$productID] = $products[$productID]; $this->config->testcase->search['params']['story']['values'] = $this->loadModel('story')->getProductStoryPairs($productID, $branch, array(), 'active,reviewing', 'id_desc', 0, '', 'story', false); @@ -2501,7 +2521,7 @@ class testcaseZen extends testcase foreach($products as $id => $name) $modules += $this->loadModel('tree')->getOptionMenu($id, 'case', 0); } - $this->config->testcase->search['params']['product']['values'] = array('') + $productList; + $this->config->testcase->search['params']['product']['values'] = $productList; $this->config->testcase->search['params']['module']['values'] = $modules; $this->config->testcase->search['params']['scene']['values'] = $this->testcase->getSceneMenu($productID, $moduleID, $branch, 0, 0, true); $this->config->testcase->search['params']['lib']['values'] = $this->loadModel('caselib')->getLibraries(); @@ -2621,7 +2641,6 @@ class testcaseZen extends testcase $fields = $this->testcase->getImportFields($productID); $fields = array_flip($fields); - $endField = end($fields); $caseData = array(); $stepData = array(); $stepVars = 0; @@ -2644,8 +2663,19 @@ class testcaseZen extends testcase $steps = (array)$cellValue; if(strpos($cellValue, "\r")) $steps = explode("\r", $cellValue); if(strpos($cellValue, "\n")) $steps = explode("\n", $cellValue); - $caseStep = $this->getImportSteps($field, $steps, $stepData, $row); + + if($stepKey == 'expect' && !empty($stepData[$row]['desc'])) + { + foreach($stepData[$row]['desc'] as $stepDescValue) + { + if(empty($stepDescValue['number'])) continue; + $caseNumber = $stepDescValue['number']; + + if($stepDescValue && !isset($caseStep[$caseNumber]) || empty($caseStep[$caseNumber]['content'])) $caseStep[$caseNumber] = ''; + } + } + $stepVars += count($caseStep, COUNT_RECURSIVE) - count($caseStep); $stepData[$row][$stepKey] = array_values($caseStep); } @@ -2722,9 +2752,9 @@ class testcaseZen extends testcase $step = trim($step); if(empty($step)) continue; - preg_match('/^((([0-9]+)[.]([0-9]+))[.]([0-9]+))[.、](.*)$/U', $step, $out); - if(!$out) preg_match('/^(([0-9]+)[.]([0-9]+))[.、](.*)$/U', $step, $out); - if(!$out) preg_match('/^([0-9]+)[.、](.*)$/U', $step, $out); + preg_match('/^((([0-9]+)[.]([0-9]+))[.]([0-9]+))[.、](.*)$/Uu', $step, $out); + if(!$out) preg_match('/^(([0-9]+)[.]([0-9]+))[.、](.*)$/Uu', $step, $out); + if(!$out) preg_match('/^([0-9]+)[.、](.*)$/Uu', $step, $out); if($out) { $count = count($out); diff --git a/module/testreport/config/table.php b/module/testreport/config/table.php index 7a436c5205..52c851c136 100644 --- a/module/testreport/config/table.php +++ b/module/testreport/config/table.php @@ -40,8 +40,8 @@ $config->testreport->dtable->fieldList['title']['data-app'] = $app->tab; $config->testreport->dtable->fieldList['execution']['name'] = 'execution'; $config->testreport->dtable->fieldList['execution']['title'] = $lang->testreport->execution; -$config->testreport->dtable->fieldList['execution']['type'] = 'text'; $config->testreport->dtable->fieldList['execution']['sortType'] = true; +$config->testreport->dtable->fieldList['execution']['width'] = '150px'; $config->testreport->dtable->fieldList['tasks']['name'] = 'taskName'; $config->testreport->dtable->fieldList['tasks']['title'] = $lang->testreport->testtask; diff --git a/module/testreport/model.php b/module/testreport/model.php index aec4dd5195..f864fd0655 100644 --- a/module/testreport/model.php +++ b/module/testreport/model.php @@ -1,7 +1,7 @@ app->getModuleRoot() . 'ai/ui/inputinject.html.php'); foreach($reports as $report) { $taskName = ''; - foreach(explode(',', $report->tasks) as $taskID) $taskName .= '#' . $taskID . $tasks[$taskID] . ' '; + foreach(explode(',', $report->tasks) as $taskID) $taskName .= '#' . $taskID . ' ' . $tasks[$taskID] . ' '; $report->taskName = $taskName; } diff --git a/module/testsuite/control.php b/module/testsuite/control.php index ee137c8f23..8e069f905a 100644 --- a/module/testsuite/control.php +++ b/module/testsuite/control.php @@ -231,7 +231,7 @@ class testsuite extends control { /* 根据suiteID更新数据,如果更新成功,则记录日志。 */ /* Update the data according to the suiteID, and record the log if the update is successful. */ - $suite = form::data($this->config->testsuite->form->edit)->add('id', $suiteID)->get(); + $suite = form::data($this->config->testsuite->form->edit, $suiteID)->add('id', $suiteID)->get(); $changes = $this->testsuite->update($suite, $this->post->uid); if(dao::isError()) return $this->send(array('result' => 'fail', 'message' => dao::getError())); diff --git a/module/testtask/control.php b/module/testtask/control.php index 1a10784ae7..833d9f3111 100644 --- a/module/testtask/control.php +++ b/module/testtask/control.php @@ -366,6 +366,8 @@ class testtask extends control /* Get testtask info. */ $testtask = $this->testtask->getByID($taskID); if(!$testtask) return $this->send(array('result' => 'fail', 'load' => array('alert' => $this->lang->testtask->checkLinked, 'locate' => array('back' => true)))); + if(!$this->loadModel('common')->checkPrivByObject('project', $testtask->project)) return $this->sendError($this->lang->project->accessDenied, inlink('browse')); + if(!$this->loadModel('common')->checkPrivByObject('execution', $testtask->execution)) return $this->sendError($this->lang->execution->accessDenied, inlink('browse')); /* 检查是否有权限访问测试单所属产品。*/ /* Check if user have permission to access the product to which the testtask belongs. */ @@ -406,6 +408,7 @@ class testtask extends control /* 从数据库中查询一个测试单下关联的测试用例。*/ /* Query the cases associated with a testtask from the database. */ $runs = $this->testtask->getTaskCases($productID, $browseType, $queryID, $moduleID, $sort, $pager, $testtask); + $runs = $this->testtask->getSceneCases($productID, $runs); $this->loadModel('common')->saveQueryCondition($this->dao->get(), 'testcase', false); $this->testtaskZen->setSearchParamsForCases($product, $moduleID, $taskID, $queryID); @@ -922,19 +925,33 @@ class testtask extends control $cases = $this->testtaskZen->prepareCasesForBatchRun($productID, $orderBy, $from, $taskID, $confirm, $caseIdList); if(empty($cases)) return $this->send(array('result' => 'fail', 'load' => array('alert' => $this->lang->testtask->skipChangedCases, 'locate' => $url))); + $steps = $this->loadModel('testcase')->getStepGroupByIdList($caseIdList); + + $emptyCases = ''; + foreach($cases as $caseID => $case) + { + if(empty($steps[$case->id])) + { + unset($cases[$caseID]); + + $emptyCases .= empty($emptyCases) ? "{$case->id}" : ",{$case->id}"; + } + } + /* 获取用例所属模块的键值对。*/ /* Get key-value pairs of case module. */ $this->loadModel('tree'); $modules = array('/'); foreach($cases as $case) $modules += $this->tree->getModulesName((array)$case->module); - $this->view->title = $this->lang->testtask->batchRun; - $this->view->steps = $this->loadModel('testcase')->getStepGroupByIdList($caseIdList); - $this->view->modules = $modules; - $this->view->cases = $cases; - $this->view->caseIdList = $caseIdList; - $this->view->productID = $productID; - $this->view->from = $from; + $this->view->title = $this->lang->testtask->batchRun; + $this->view->steps = $this->loadModel('testcase')->getStepGroupByIdList($caseIdList); + $this->view->modules = $modules; + $this->view->cases = $cases; + $this->view->caseIdList = $caseIdList; + $this->view->productID = $productID; + $this->view->from = $from; + $this->view->emptyCases = $emptyCases; $this->display(); } diff --git a/module/testtask/js/common.ui.js b/module/testtask/js/common.ui.js index a7315fffc9..6dc41006c4 100644 --- a/module/testtask/js/common.ui.js +++ b/module/testtask/js/common.ui.js @@ -131,7 +131,7 @@ function createBug(event) }); var link = $.createLink('bug', 'create', $form.data('params') + ',stepIdList=' + stepIdList); - openPage(link, 'qa'); + openUrl(link, {load: 'modal'}); $('#runCaseModal').closest('.modal').off('hide.zui.modal'); $('#casesResults').closest('.modal').off('hide.zui.modal'); diff --git a/module/testtask/js/runcase.ui.js b/module/testtask/js/runcase.ui.js index 468d030623..6aa9e3dbf1 100644 --- a/module/testtask/js/runcase.ui.js +++ b/module/testtask/js/runcase.ui.js @@ -18,6 +18,7 @@ $('#runCaseModal').closest('.modal').off('hide.zui.modal').on('hide.zui.modal', function loadResult() { loadCurrentPage({url: resultsLink, selector: '#casesResults', partial: true}); + window.waitDom('#casesResults .result-item', function(){ $('#casesResults .result-item').first().trigger('click');}) } /** diff --git a/module/testtask/lang/de.php b/module/testtask/lang/de.php index 437b5eb248..1a0a73cf2d 100644 --- a/module/testtask/lang/de.php +++ b/module/testtask/lang/de.php @@ -120,6 +120,7 @@ $lang->testtask->pageSummary = 'Total testtasks: %s.'; $lang->testtask->mySummary = 'Total testtasks: %s, Wait: %s, Testing: %s, Blocked: %s.'; $lang->testtask->allSummary = 'Total testtasks: %s, Wait: %s, Testing: %s, Blocked: %s, Done: %s.'; $lang->testtask->checkedAllSummary = 'Seleted: %total%, Wait: %wait%, Testing: %testing%, Blocked: %blocked%, Done: %done%.'; +$lang->testtask->emptyCases = 'Cases %s have no steps, filter to show.'; $lang->testtask->beginAndEnd = 'Datum'; $lang->testtask->to = 'An'; diff --git a/module/testtask/lang/en.php b/module/testtask/lang/en.php index a00566d362..c5e460e19f 100644 --- a/module/testtask/lang/en.php +++ b/module/testtask/lang/en.php @@ -120,6 +120,7 @@ $lang->testtask->pageSummary = 'Total testtasks: %s.'; $lang->testtask->mySummary = 'Total testtasks: %s, Wait: %s, Testing: %s, Blocked: %s.'; $lang->testtask->allSummary = 'Total testtasks: %s, Wait: %s, Testing: %s, Blocked: %s, Done: %s.'; $lang->testtask->checkedAllSummary = 'Seleted: %total%, Wait: %wait%, Testing: %testing%, Blocked: %blocked%.'; +$lang->testtask->emptyCases = 'Cases %s have no steps, filter to show.'; $lang->testtask->beginAndEnd = 'Duration'; $lang->testtask->to = 'To'; diff --git a/module/testtask/lang/fr.php b/module/testtask/lang/fr.php index 1ed96152c6..c2698a047e 100644 --- a/module/testtask/lang/fr.php +++ b/module/testtask/lang/fr.php @@ -120,6 +120,7 @@ $lang->testtask->pageSummary = 'Total testtasks: %s.'; $lang->testtask->mySummary = 'Total testtasks: %s, Wait: %s, Testing: %s, Blocked: %s.'; $lang->testtask->allSummary = 'Total testtasks: %s, Wait: %s, Testing: %s, Blocked: %s, Done: %s.'; $lang->testtask->checkedAllSummary = 'Seleted: %total%, Wait: %wait%, Testing: %testing%, Blocked: %blocked%.'; +$lang->testtask->emptyCases = 'Cases %s have no steps, filter to show.'; $lang->testtask->beginAndEnd = 'Durée'; $lang->testtask->to = 'à'; diff --git a/module/testtask/lang/zh-cn.php b/module/testtask/lang/zh-cn.php index 214307554d..909539f1d2 100644 --- a/module/testtask/lang/zh-cn.php +++ b/module/testtask/lang/zh-cn.php @@ -120,6 +120,7 @@ $lang->testtask->pageSummary = '本页共 %s 个测试单 $lang->testtask->mySummary = '本页共 %s 个测试单,待测试 %s,测试中 %s,被阻塞 %s。'; $lang->testtask->allSummary = '本页共 %s 个测试单,待测试 %s,测试中 %s,被阻塞 %s,已测试 %s。'; $lang->testtask->checkedAllSummary = '共选中 %total% 个测试单,待测试 %wait%,测试中 %testing%,被阻塞 %blocked%,已测试 %done%。'; +$lang->testtask->emptyCases = 'ID %s 的用例没有步骤,过滤显示。'; $lang->testtask->beginAndEnd = '起止时间'; $lang->testtask->to = '至'; diff --git a/module/testtask/model.php b/module/testtask/model.php index bbae21d039..e9123318d4 100755 --- a/module/testtask/model.php +++ b/module/testtask/model.php @@ -1040,6 +1040,54 @@ class testtaskModel extends model ->fetchAll('id'); } + /** + * 获取用例以及场景数据。 + * Get cases with scenes data. + * + * @param int $productID + * @param array $runs + * @access public + * @return array + */ + public function getSceneCases($productID, $runs) + { + $scenes = $this->dao->select('*')->from(TABLE_SCENE) + ->where('deleted')->eq('0') + ->andWhere('product')->eq($productID) + ->orderBy('grade_desc, sort_asc') + ->fetchAll('id'); + + $displayScenes = array(); + foreach($runs as $run) + { + if(!empty($run->scene)) $displayScenes[] = $run->scene; + $run->parent = !empty($run->scene) ? 'scene-' . $scenes[$run->scene]->id : 0; + $run->isScene = false; + } + + foreach($scenes as $scene) + { + foreach($displayScenes as $displayScene) + { + if(str_contains($scene->path, $displayScene . ',')) + { + $displayScenes += explode(',', trim($scene->path, ',')); + } + } + } + + $displayScenes = array_unique($displayScenes); + foreach($scenes as $id => $scene) + { + if(!in_array($id, $displayScenes)) unset($scenes[$id]); + $scene->parent = !empty($scene->parent) ? 'scene-' . $scene->parent : 0; + $scene->id = 'scene-' . $scene->id; + $scene->isScene = true; + } + + return array_merge($runs, $scenes); + } + /** * 获取一个测试单关联的用例。 * Get cases associated with a testtask. @@ -1741,7 +1789,7 @@ class testtaskModel extends model $suiteCase = new stdclass(); $testRun = new stdclass(); $testRun->task = $taskID; - $testRun->status = 'done'; + $testRun->status = 'normal'; $existSuites = $suiteNames ? $this->getExistSuitesOfUnitResult($suiteNames, $productID, $auto) : array(); @@ -2207,7 +2255,7 @@ class testtaskModel extends model $case->steps = $steps; $case->lastRunResult = $result->caseResult; if(isset($caseResult->id)) $case->id = $caseResult->id; - if(!isset($caseResult->id)) $case->product = $productID; + $case->product = $productID; $caseTitles[$suiteIndex][] = $case->title; $cases[$suiteIndex][$caseIndex] = $case; diff --git a/module/testtask/ui/batchrun.html.php b/module/testtask/ui/batchrun.html.php index 5d2d194c39..a619fd5f5e 100644 --- a/module/testtask/ui/batchrun.html.php +++ b/module/testtask/ui/batchrun.html.php @@ -15,6 +15,12 @@ if(count($cases) != count($caseIdList)) h::js("zui.Modal.alert('{$lang->testtask->skipChangedCases}');"); } +if(!empty($emptyCases)) +{ + $emptyCasesTip = sprintf($lang->testtask->emptyCases, $emptyCases); + pageJS("zui.Modal.alert({message: '{$emptyCasesTip}', icon: 'icon-exclamation-sign', iconClass: 'warning-pale rounded-full icon-2x'});\n"); +} + unset($lang->testcase->resultList['n/a']); $caseItems = array(); diff --git a/module/testtask/ui/cases.html.php b/module/testtask/ui/cases.html.php index d1557f130e..c81ded854d 100644 --- a/module/testtask/ui/cases.html.php +++ b/module/testtask/ui/cases.html.php @@ -152,12 +152,14 @@ if(isset($cols['title']['link']['params'])) $cols['title']['link']['params'] = ' if(isset($cols['bugs']['link']['params'])) $cols['bugs']['link']['params'] = 'caseID={case}'; if(isset($cols['scene'])) $cols['scene']['map'] = $iscenes; if(isset($cols['status'])) $cols['status']['statusMap']['changed'] = $lang->testcase->changed; +if(isset($cols['title'])) $cols['title']['nestedToggle'] = true; $runs = initTableData($runs, $cols); $runs = array_map( function($run) { if($run->version < $run->caseVersion) $run->status = 'changed'; + if($run->isScene) unset($run->actions); return $run; }, $runs @@ -167,6 +169,7 @@ dtable ( set::customCols(true), set::userMap($users), + set::nested(true), set::cols($cols), set::data($runs), set::orderBy($orderBy), diff --git a/module/testtask/ui/results.html.php b/module/testtask/ui/results.html.php index 45f186652b..0edf584d5e 100644 --- a/module/testtask/ui/results.html.php +++ b/module/testtask/ui/results.html.php @@ -264,7 +264,6 @@ foreach($results as $i => $result) ( setClass('btn h-7 px-6 ml-8 to-bug-button'), set::type('primary'), - set('data-dismiss', 'modal'), on::click('createBug'), $lang->testcase->createBug ) diff --git a/module/testtask/ui/runcase.html.php b/module/testtask/ui/runcase.html.php index 089df33add..5f11fae4a9 100644 --- a/module/testtask/ui/runcase.html.php +++ b/module/testtask/ui/runcase.html.php @@ -30,18 +30,95 @@ if($confirm != 'yes') if(empty($steps)) { $stepTrs[] = h::tr + ( + setClass("step"), + h::td ( - h::td + setClass('text-left border'), + div ( - setClass('border'), - set('colspan', '4'), - div + setClass('inputGroup row', 'pl-0'), + h::span ( - setClass('text-center text-gray py-4'), - $lang->testcase->noStep + setClass('step-item-id mr-2'), + ), + div(html()) + ) + ), + h::td + ( + setClass('text-left border'), + html() + ), + h::td + ( + setClass('result-td'), + picker + ( + on::change('checkStepValue'), + set::name("result[0]"), + set::items($lang->testcase->resultList), + set::value('pass'), + set::required() + ) + ), + h::td + ( + setClass('real-td'), + h::table + ( + setClass('w-full'), + h::tr + ( + h::td + ( + setClass('p-0 bd-0'), + textarea + ( + on::keyup('realChange'), + setClass('leading-4 w-60' ), + set('rows', '1'), + set::name("real[0]") + ) + ), + h::td + ( + setClass('p-0 bd-0'), + width('40px'), + btn + ( + setClass('ml-2 text-primary'), + set::target("#fileModal0"), + set('data-toggle', 'modal'), + on::click('setFileModalHeight'), + set('title', $lang->testtask->files), + set::icon('paper-clip'), + ) + ) ) ) - ); + ) + ); + $fileModals[] = modal + ( + set::id("fileModal0"), + set::title($lang->testtask->files), + setData('position', 'center'), + fileSelector + ( + set::name("files0[]") + ), + div + ( + setClass('text-center'), + btn + ( + setClass('btn-wide primary'), + set('data-dismiss', 'modal'), + $lang->save + ) + ) + ); } foreach($steps as $key => $step) { @@ -218,7 +295,7 @@ form set('data-load', 'modal'), $lang->testtask->pre ) : '', - $run->case->status != 'wait' && $confirm != 'yes' && !empty($run->case->steps) ? btn + $run->case->status != 'wait' && $confirm != 'yes' ? btn ( setClass('primary btn-wide w-24 m-3'), set::btnType('submit'), diff --git a/module/testtask/zen.php b/module/testtask/zen.php index de97f2a17c..f37d786f62 100644 --- a/module/testtask/zen.php +++ b/module/testtask/zen.php @@ -151,7 +151,7 @@ class testtaskZen extends testtask */ protected function buildTaskForEdit(int $taskID, int $productID): object { - $task = form::data($this->config->testtask->form->edit) + $task = form::data($this->config->testtask->form->edit, $taskID) ->add('id', $taskID) ->add('product', $productID) ->stripTags($this->config->testtask->editor->edit['id'], $this->config->allowedTags) @@ -185,7 +185,7 @@ class testtaskZen extends testtask */ protected function buildTaskForStart(int $taskID): object { - $task = form::data($this->config->testtask->form->start) + $task = form::data($this->config->testtask->form->start, $taskID) ->add('id', $taskID) ->stripTags($this->config->testtask->editor->start['id'], $this->config->allowedTags) ->get(); @@ -202,7 +202,7 @@ class testtaskZen extends testtask */ protected function buildTaskForClose(int $taskID): object { - $task = form::data($this->config->testtask->form->close) + $task = form::data($this->config->testtask->form->close, $taskID) ->add('id', $taskID) ->stripTags($this->config->testtask->editor->close['id'], $this->config->allowedTags) ->get(); @@ -219,7 +219,7 @@ class testtaskZen extends testtask */ protected function buildTaskForActivate(int $taskID): object { - $task = form::data($this->config->testtask->form->activate) + $task = form::data($this->config->testtask->form->activate, $taskID) ->add('id', $taskID) ->stripTags($this->config->testtask->editor->activate['id'], $this->config->allowedTags) ->get(); @@ -236,7 +236,7 @@ class testtaskZen extends testtask */ protected function buildTaskForBlock(int $taskID): object { - $task = form::data($this->config->testtask->form->block) + $task = form::data($this->config->testtask->form->block, $taskID) ->add('id', $taskID) ->stripTags($this->config->testtask->editor->block['id'], $this->config->allowedTags) ->get(); @@ -703,7 +703,7 @@ class testtaskZen extends testtask { if($caseResult == 'fail') { - $link = inlink('results',"runID=$runID&caseID=$caseID&version=$version"); + $link = inlink('runCase', "runID={$runID}&caseID={$caseID}&version={$version}"); return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'callback' => "loadModal('$link', 'runCaseModal')")); } diff --git a/module/todo/control.php b/module/todo/control.php index 73d826545e..4897942fe9 100755 --- a/module/todo/control.php +++ b/module/todo/control.php @@ -520,7 +520,6 @@ class todo extends control $assemble->risks = $iroData[1]; $assemble->opportunities = $iroData[2]; } - if(isset($this->config->qcVersion)) $assemble->reviews = $this->todoZen->exportAssociated('qcVersion', (string)$user->account); $todos = $this->todoZen->assembleExportData((array)$todos, $assemble, $todoLang, (array)$times); diff --git a/module/todo/model.php b/module/todo/model.php index 3ef2ce04fe..32c194c927 100755 --- a/module/todo/model.php +++ b/module/todo/model.php @@ -221,10 +221,10 @@ class todoModel extends model */ public function getList(string $type = 'today', string $account = '', string|array $status = 'all', int $limit = 0, object $pager = null, string $orderBy="date,status,begin"): array { - $type = strtolower($type); + $this->app->loadClass('date', true); + $type = strtolower($type); $dateRange = $this->dateRange($type); - if(empty($account)) $account = $this->app->user->account; $todos = $this->todoTao->getListBy($type, $account, $status, (string)$dateRange['begin'], (string)$dateRange['end'], $limit, $orderBy, $pager); @@ -254,24 +254,20 @@ class todoModel extends model * @access protected * @return array */ - protected function dateRange($type): array + protected function dateRange(string $type): array { - $this->app->loadClass('date'); - $dateRange['all'] = array('begin' => '1970-01-01', 'end' => '2109-01-01'); - $dateRange['assignedtoother'] = array('begin' => '1970-01-01', 'end' => '2109-01-01'); - $dateRange['today'] = array('begin' => date::today(), 'end' => date::today()); - $dateRange['future'] = array('begin' => '2030-01-01', 'end' => '2030-01-01'); - $dateRange['before'] = array('begin' => '1970-01-01', 'end' => date::today()); - $dateRange['cycle'] = array('begin' => '', 'end' => ''); - $dateRange['yesterday'] = array('begin' => date::yesterday(), 'end' => date::yesterday()); - $dateRange['thisweek'] = array('begin' => date::getThisWeek()['begin'], 'end' => date::getThisWeek()['end']); - $dateRange['lastweek'] = array('begin' => date::getLastWeek()['begin'], 'end' => date::getLastWeek()['end']); - $dateRange['thismonth'] = array('begin' => date::getThisMonth()['begin'], 'end' => date::getThisMonth()['end']); - $dateRange['lastmonth'] = array('begin' => date::getLastMonth()['begin'], 'end' => date::getLastMonth()['end']); - $dateRange['thisseason'] = array('begin' => date::getThisSeason()['begin'], 'end' => date::getThisSeason()['end']); - $dateRange['thisyear'] = array('begin' => date::getThisYear()['begin'], 'end' => date::getThisYear()['end']); + if($type == 'future') return array('begin' => '2030-01-01', 'end' => '2030-01-01'); - return isset($dateRange[$type]) ? $dateRange[$type] : array('begin' => $type, 'end' => $type); + if(strpos(',before,today,yesterday,thisweek,lastweek,thismonth,lastmonth,thisseason,thisyear,', ",$type,") === false) return array('begin' => '', 'end' => ''); + + if($type == 'before') return array('begin' => '', 'end' => date::yesterday()); + + $funcName = ($type == 'today' || $type == 'yesterday') ? $type : 'get' . ucfirst($type); + $dateRange = date::$funcName(); + + if($type == 'today' || $type == 'yesterday') return array('begin' => $dateRange, 'end' => $dateRange); + + return $dateRange; } /** diff --git a/module/todo/zen.php b/module/todo/zen.php index 90db826d93..1d2425824c 100755 --- a/module/todo/zen.php +++ b/module/todo/zen.php @@ -855,6 +855,7 @@ class todoZen extends todo $todo->end = $todo->end == '2400' ? '' : $end; $todo->assignedTo = zget($assemble->users, $todo->assignedTo); + if($todo->date == '2030-01-01') $todo->date = $this->lang->todo->future; $type = $todo->type; if(isset($assemble->users[$todo->account])) $todo->account = $assemble->users[$todo->account]; @@ -872,7 +873,6 @@ class todoZen extends todo if($type == 'risk') $todo->name = isset($assemble->risks[$todo->objectID]) ? $assemble->risks[$todo->objectID] . "(#$todo->objectID)" : ''; if($type == 'opportunity') $todo->name = isset($assemble->opportunities[$todo->objectID]) ? $assemble->opportunities[$todo->objectID] . "(#$todo->objectID)" : ''; } - if($type == 'review' && isset($this->config->qcVersion)) $todo->name = isset($assemble->reviews[$todo->objectID]) ? $assemble->reviews[$todo->objectID] . "(#$todo->objectID)" : ''; if(isset($todoLang->typeList[$type])) $todo->type = $todoLang->typeList[$type]; if(isset($todoLang->priList[$todo->pri])) $todo->pri = $todoLang->priList[$todo->pri]; diff --git a/module/tree/model.php b/module/tree/model.php index 96ce78268a..7cba38e45a 100644 --- a/module/tree/model.php +++ b/module/tree/model.php @@ -533,7 +533,7 @@ class treeModel extends model if($productNum > 1) { $menuItem = new stdclass(); - $menuItem->id = $id; + $menuItem->id = "product-$id"; $menuItem->name = $product; $menuItem->parent = 0; $menuItem->url = helper::createLink('execution', 'task', "executionID=$rootID&status=byProduct&praram=$id"); @@ -554,6 +554,7 @@ class treeModel extends model { if(!isset($executionModules[$module->id]) && strpos($extra['extra'], 'allModule') === false) continue; + if($module->type == 'story' && $module->root > 0) $module->parent = "product-{$module->root}"; $module->url = helper::createLink('execution', 'task', "executionID={$rootID}&type=byModule¶m={$module->id}"); $menu[$module->id] = $module; } @@ -576,7 +577,8 @@ class treeModel extends model $menu[$module->id] = $module; } } - return array_values($menu); + + return $menu; } /** @@ -1882,6 +1884,7 @@ class treeModel extends model } else { + $originID = $moduleID; $short = $shorts[$moduleID]; $order = $orders[$moduleID]; $moduleID = (int)str_replace('id', '', $moduleID); @@ -1889,9 +1892,10 @@ class treeModel extends model $oldModule = $this->getByID($moduleID); $data = new stdClass(); - $data->name = strip_tags(trim($moduleName)); - $data->short = $short; - $data->order = $order; + $data->name = strip_tags(trim($moduleName)); + $data->short = $short; + $data->order = $order; + $data->branch = isset($branches[$originID]) ? $branches[$originID] : 0; $this->setModuleLang(); $this->dao->update(TABLE_MODULE)->data($data)->autoCheck()->where('id')->eq($moduleID)->limit(1)->exec(); diff --git a/module/tutorial/model.php b/module/tutorial/model.php index 8211854e7e..c123884e6d 100644 --- a/module/tutorial/model.php +++ b/module/tutorial/model.php @@ -1,7 +1,7 @@ id = 1; - $product->program = 0; - $product->line = 0; - $product->plan = 0; - $product->name = 'Test product'; - $product->code = 'test'; - $product->type = 'normal'; - $product->status = 'normal'; - $product->desc = ''; - $product->shadow = '0'; - $product->PO = $this->app->user->account; - $product->QD = ''; - $product->RD = ''; - $product->acl = 'open'; - $product->createdBy = $this->app->user->account; - $product->createdDate = helper::now(); - $product->createdVersion = '8.1.3'; - $product->order = 10; - $product->deleted = '0'; - $product->branch = ''; - $product->reviewer = $this->app->user->account; - $product->branches = array(); - $product->plans = array('1' => 'Test plan'); + $product->id = 1; + $product->program = 0; + $product->line = 0; + $product->plan = 0; + $product->name = 'Test product'; + $product->code = 'test'; + $product->type = 'normal'; + $product->status = 'normal'; + $product->desc = ''; + $product->shadow = '0'; + $product->PO = $this->app->user->account; + $product->QD = ''; + $product->RD = ''; + $product->acl = 'open'; + $product->createdBy = $this->app->user->account; + $product->createdDate = helper::now(); + $product->createdVersion = '8.1.3'; + $product->order = 10; + $product->deleted = '0'; + $product->branch = ''; + $product->reviewer = $this->app->user->account; + $product->branches = array(); + $product->plans = array('1' => 'Test plan'); + $product->totalEpics = 0; + $product->totalRequirements = 0; return $product; } diff --git a/module/upgrade/config.php b/module/upgrade/config.php index f377800dc2..047b4359aa 100644 --- a/module/upgrade/config.php +++ b/module/upgrade/config.php @@ -52,7 +52,9 @@ $config->upgrade->maxVersion['max5_0_0'] = '20_1_1'; $config->upgrade->maxVersion['max5_1_0'] = '20_2_0'; $config->upgrade->maxVersion['max5_2_0'] = '20_3_0'; $config->upgrade->maxVersion['max5_3'] = '20_4'; -$config->upgrade->maxVersion['max5_4'] = '20_5'; // max insert position. +$config->upgrade->maxVersion['max4_13'] = '18_13'; +$config->upgrade->maxVersion['max5_4'] = '20_5'; +$config->upgrade->maxVersion['max5_5'] = '20_6'; // max insert position. $config->upgrade->bizVersion = array(); $config->upgrade->bizVersion['biz1_0'] = '9_5_1'; @@ -143,7 +145,9 @@ $config->upgrade->bizVersion['biz10_0_0'] = '20_1_1'; $config->upgrade->bizVersion['biz10_1_0'] = '20_2_0'; $config->upgrade->bizVersion['biz10_2_0'] = '20_3_0'; $config->upgrade->bizVersion['biz10_3'] = '20_4'; -$config->upgrade->bizVersion['biz10_4'] = '20_5'; // biz insert position. +$config->upgrade->maxVersion['biz8_13'] = '18_13'; +$config->upgrade->bizVersion['biz10_4'] = '20_5'; +$config->upgrade->bizVersion['biz10_5'] = '20_6'; // biz insert position. $config->upgrade->proVersion = array(); $config->upgrade->proVersion['pro1_0'] = '3_1'; @@ -271,7 +275,9 @@ $config->upgrade->ipdVersion['ipd2_0_0'] = '20_1_1'; $config->upgrade->ipdVersion['ipd2_1_0'] = '20_2_0'; $config->upgrade->ipdVersion['ipd2_2_0'] = '20_3_0'; $config->upgrade->ipdVersion['ipd2_3'] = '20_4'; -$config->upgrade->ipdVersion['ipd2_4'] = '20_5'; // ipd insert position. +$config->upgrade->maxVersion['ipd1_5'] = '18_13'; +$config->upgrade->ipdVersion['ipd2_4'] = '20_5'; +$config->upgrade->ipdVersion['ipd2_5'] = '20_6'; // ipd insert position. $config->upgrade->lowerTables = array(); $config->upgrade->lowerTables[$config->db->prefix . 'caseStep'] = $config->db->prefix . 'casestep'; @@ -1296,6 +1302,8 @@ $config->delete['20_4'][] = 'extension/ipd/repo/ext/model/zentaomax.php'; $config->delete['20_4'][] = 'extension/ipd/repo/ext/view/log.html.php'; $config->delete['20_4'][] = 'extension/xuan/index/ext/config/xuan.php'; +$config->delete['20_5'][] = 'extension/custom/my/ext/control/preference.php'; + $config->upgrade->openModules = array('action', 'admin', 'ai', 'bi', 'aiapp', 'api', 'automation', 'backup', 'block', 'branch', 'budget', 'bug', 'build', 'caselib', 'chart', 'ci', 'client', 'common', 'company', 'compile', 'convert', 'cron', 'custom', 'datatable', 'dataview', 'dept', 'design', 'dev', 'dimension', 'doc', 'durationestimation', 'entry', 'execution', 'extension', 'file', 'git', 'gitlab', 'group', 'holiday', 'im', 'index', 'index.html', 'install', 'issue', 'jenkins', 'job', 'kanban', 'license', 'mail', 'message', 'metric', 'misc', 'mr', 'my', 'personnel', 'pipeline', 'product', 'productplan', 'productset', 'program', 'programplan', 'project', 'projectbuild', 'projectplan', 'projectrelease', 'projectstory', 'pivot', 'qa', 'release', 'repo', 'report', 'risk', 'score', 'screen', 'search', 'setting', 'sonarqube', 'sso', 'stage', 'stakeholder', 'story', 'subject', 'svn', 'task', 'testcase', 'testreport', 'testsuite', 'testtask', 'todo', 'tree', 'tutorial', 'upgrade', 'user', 'webhook', 'weekly', 'workestimation', 'gitea', 'gogs', 'transfer', 'zahost', 'zanode', 'editor', 'charter', 'roadmap', 'account', 'cne', 'host', 'instance', 'ops', 'serverroom', 'space', 'store', 'system', 'solution', 'demand', 'gitfox', 'epic', 'requirement'); $config->upgrade->unsetModules = array('design', 'program', 'programplan', 'projectbuild', 'projectrelease', 'stage', 'stakeholder', 'product', 'branch', 'productplan', 'release', 'build', 'qa', 'bug', 'testcase', 'testtask', 'testreport', 'testsuite', 'caselib', 'automation', 'repo', 'ci', 'compile', 'jenkins', 'job', 'svn', 'gitlab', 'sonarqube', 'mr', 'git', 'report', 'sqlbuilder', 'feedback', 'faq', 'attend', 'holiday', 'leave', 'makeup', 'overtime', 'lieu', 'ops', 'host', 'serverroom', 'account', 'domain', 'service', 'deploy', 'conference', 'traincourse', 'pssp', 'baseline', 'classify', 'cm', 'cmcl', 'auditcl', 'reviewcl', 'process', 'activity', 'zoutput', 'auditplan', 'nc', 'subject', 'weekly', 'workestimation', 'issue', 'durationestimation', 'risk', 'opportunity', 'trainplan', 'gapanalysis', 'researchplan', 'researchreport', 'meeting', 'meetingroom', 'budget', 'reviewissue', 'reviewsetting', 'review', 'milestone', 'measurement', 'measrecord', 'assetlib', 'setting', 'im', 'client', 'ldap', 'dev', 'api', 'gitea', 'gogs', 'zanode', 'zahost'); diff --git a/module/upgrade/config/upgradeflow.php b/module/upgrade/config/upgradeflow.php index c42ada3a02..e369c30ef9 100644 --- a/module/upgrade/config/upgradeflow.php +++ b/module/upgrade/config/upgradeflow.php @@ -176,3 +176,4 @@ if(!empty($config->isINT)) } $config->upgrade->execFlow['max2_2'] = array('functions' => 'addDefaultKanbanPri'); +$config->upgrade->execFlow['max5_4'] = array('functions' => 'updateApprovalCondition'); diff --git a/module/upgrade/control.php b/module/upgrade/control.php index 3a3cf6827b..b350f23543 100644 --- a/module/upgrade/control.php +++ b/module/upgrade/control.php @@ -774,6 +774,19 @@ class upgrade extends control echo 'ok'; } + /** + * 升级度量项内置数据。 + * Upgrade metric built-in data. + * + * @access public + * @return void + */ + public function ajaxUpgradeMetricData() + { + $this->upgrade->upgradeMetricData(); + echo 'ok'; + } + /** * 安装DuckDB引擎。 * AJAX: Install duckdb. diff --git a/module/upgrade/lang/version.php b/module/upgrade/lang/version.php index a510490d20..7da69e630d 100644 --- a/module/upgrade/lang/version.php +++ b/module/upgrade/lang/version.php @@ -188,6 +188,7 @@ $lang->upgrade->fromVersions['18_10'] = '18.10'; $lang->upgrade->fromVersions['18_10_1'] = '18.10.1'; $lang->upgrade->fromVersions['18_11'] = '18.11'; $lang->upgrade->fromVersions['18_12'] = '18.12'; +$lang->upgrade->fromVersions['18_13'] = '18.13'; $lang->upgrade->fromVersions['20_0_alpha1'] = '20.0.alpha1'; $lang->upgrade->fromVersions['20_0_beta1'] = '20.0.beta1'; $lang->upgrade->fromVersions['20_0_beta2'] = '20.0.beta2'; @@ -197,7 +198,8 @@ $lang->upgrade->fromVersions['20_1_1'] = '20.1.1'; $lang->upgrade->fromVersions['20_2_0'] = '20.2.0'; $lang->upgrade->fromVersions['20_3_0'] = '20.3.0'; $lang->upgrade->fromVersions['20_4'] = '20.4'; -$lang->upgrade->fromVersions['20_5'] = '20.5'; // pms insert position. +$lang->upgrade->fromVersions['20_5'] = '20.5'; +$lang->upgrade->fromVersions['20_6'] = '20.6'; // pms insert position. global $config; /* Lite. */ @@ -390,12 +392,14 @@ $lang->upgrade->fromVersions['biz8_10'] = 'Biz8.10'; $lang->upgrade->fromVersions['biz8_10_1'] = 'Biz8.10.1'; $lang->upgrade->fromVersions['biz8_11'] = 'Biz8.11'; $lang->upgrade->fromVersions['biz8_12'] = 'Biz8.12'; +$lang->upgrade->fromVersions['biz8_13'] = 'Biz8.13'; $lang->upgrade->fromVersions['biz10_0_0_beta1'] = 'Biz10.0.0.beta1'; $lang->upgrade->fromVersions['biz10_0_0'] = 'Biz10.0.0'; $lang->upgrade->fromVersions['biz10_1_0'] = 'Biz10.1.0'; $lang->upgrade->fromVersions['biz10_2_0'] = 'Biz10.2.0'; $lang->upgrade->fromVersions['biz10_3'] = 'Biz10.3'; -$lang->upgrade->fromVersions['biz10_4'] = 'Biz10.4'; // biz insert position. +$lang->upgrade->fromVersions['biz10_4'] = 'Biz10.4'; +$lang->upgrade->fromVersions['biz10_5'] = 'Biz10.5'; // biz insert position. /* Max. */ $lang->upgrade->fromVersions['max2_0_beta4'] = 'Max2.0.beta4'; @@ -446,12 +450,14 @@ $lang->upgrade->fromVersions['max4_10'] = 'Max4.10'; $lang->upgrade->fromVersions['max4_10_1'] = 'Max4.10.1'; $lang->upgrade->fromVersions['max4_11'] = 'Max4.11'; $lang->upgrade->fromVersions['max4_12'] = 'Max4.12'; +$lang->upgrade->fromVersions['max4_13'] = 'Max4.13'; $lang->upgrade->fromVersions['max5_0_0_beta1'] = 'Max5.0.0.beta1'; $lang->upgrade->fromVersions['max5_0_0'] = 'Max5.0.0'; $lang->upgrade->fromVersions['max5_1_0'] = 'Max5.1.0'; $lang->upgrade->fromVersions['max5_2_0'] = 'Max5.2.0'; $lang->upgrade->fromVersions['max5_3'] = 'Max5.3'; -$lang->upgrade->fromVersions['max5_4'] = 'Max5.4'; // max insert position. +$lang->upgrade->fromVersions['max5_4'] = 'Max5.4'; +$lang->upgrade->fromVersions['max5_5'] = 'Max5.5'; // max insert position. /* Ipd */ $lang->upgrade->fromVersions['ipd1_0_beta1'] = 'Ipd1.0.beta1'; @@ -464,8 +470,10 @@ $lang->upgrade->fromVersions['ipd1_1_2'] = 'Ipd1.1.2'; $lang->upgrade->fromVersions['ipd1_2'] = 'Ipd1.2'; $lang->upgrade->fromVersions['ipd1_3'] = 'Ipd1.3'; $lang->upgrade->fromVersions['ipd1_4'] = 'Ipd1.4'; +$lang->upgrade->fromVersions['ipd1_5'] = 'Ipd1.5'; $lang->upgrade->fromVersions['ipd2_0_0_beta1'] = 'Ipd2.0.0.beta1'; $lang->upgrade->fromVersions['ipd2_0_0'] = 'Ipd2.0.0'; $lang->upgrade->fromVersions['ipd2_1_0'] = 'Ipd2.1.0'; $lang->upgrade->fromVersions['ipd2_2_0'] = 'Ipd2.2.0'; -$lang->upgrade->fromVersions['ipd2_3'] = 'Ipd2.3'; // ipd insert position. +$lang->upgrade->fromVersions['ipd2_3'] = 'Ipd2.3'; +$lang->upgrade->fromVersions['ipd2_4'] = 'Ipd2.4'; // ipd insert position. diff --git a/module/upgrade/model.php b/module/upgrade/model.php index f045d853ea..0a45f925d9 100644 --- a/module/upgrade/model.php +++ b/module/upgrade/model.php @@ -185,6 +185,7 @@ class upgradeModel extends model $this->loadModel('product')->refreshStats(true); $this->deletePatch(); $this->processDataset(); + $this->upgradeMetricData(); if($fromEdition == 'open') { @@ -9113,6 +9114,42 @@ class upgradeModel extends model return true; } + /** + * 更新升级度量项数据。 + * Import metric data. + * + * @access public + * @return bool + */ + public function upgradeMetricData() + { + $this->saveLogs('Run Method ' . __FUNCTION__); + $this->dao->clearTablesDescCache(); + + $metricSQLs = $this->loadModel('bi')->prepareBuiltinMetricSQL('update'); + try + { + foreach($metricSQLs as $sql) + { + $sql = trim($sql); + if(empty($sql)) continue; + + $this->saveLogs($sql); + + $sql = str_replace('zt_', $this->config->db->prefix, $sql); + $this->dbh->query($sql); + if(dao::isError()) return false; + } + } + catch(Error $e) + { + a($e->getMessage()); + die; + } + + return true; + } + /** * 更新升级BI内置数据。 * Import BI data. @@ -9135,9 +9172,8 @@ class upgradeModel extends model $pivotSQLs = $this->bi->prepareBuiltinPivotSQL('update'); $upgradeSqls = array_merge($upgradeSqls, $chartSQLs, $pivotSQLs); } - $metricSQLs = $this->bi->prepareBuiltinMetricSQL('update'); $screenSQLs = $this->bi->prepareBuiltinScreenSQL('update'); - $upgradeSqls = array_merge($upgradeSqls, $metricSQLs, $screenSQLs); + $upgradeSqls = array_merge($upgradeSqls, $screenSQLs); try { @@ -9701,4 +9737,115 @@ class upgradeModel extends model return !dao::isError(); } + + /** + * 更新审批流的条件分支数据结构。 + * Update the condition branch data structure of the approval flow. + * + * @access public + * @return void + */ + public function updateApprovalCondition() + { + $specs = $this->dao->select('id, nodes')->from(TABLE_APPROVALFLOWSPEC)->fetchPairs(); + + foreach($specs as $specID => $spec) + { + $nodes = json_decode($spec); + + $hasBranch = false; + foreach($nodes as $id => $node) + { + if($node->type != 'branch') continue; + + $hasBranch = true; + $nodes[$id] = $this->transferCondition($node); + } + + if($hasBranch) $this->dao->update(TABLE_APPROVALFLOWSPEC)->set('nodes')->eq(json_encode($nodes))->where('id')->eq($specID)->exec(); + } + } + + /** + * 递归转换条件分支的数据结构。 + * Transfer branch conditions. + * + * @param object $node + * @access public + * @return object + */ + public function transferCondition($node) + { + foreach($node->branches as $index => $branch) + { + if(isset($branch->conditions)) + { + $newConditions = array(); + $newCondition = new stdClass(); + $newCondition->conditionOperator = 'equal'; + $newCondition->conditionLogical = 'or'; + foreach($branch->conditions as $condition) + { + if($condition->selectType == 'account') + { + foreach($condition->users as $account) + { + $newCondition->conditionField = 'submitUsers'; + $newCondition->conditionValue = $account; + $newConditions[] = clone $newCondition; + } + } + elseif($condition->selectType == 'role') + { + foreach($condition->roles as $role) + { + $newCondition->conditionField = 'submitRoles'; + $newCondition->conditionValue = $role; + $newConditions[] = clone $newCondition; + } + } + elseif($condition->selectType == 'position') + { + foreach($condition->positions as $position) + { + $newCondition->conditionField = 'submitPositions'; + $newCondition->conditionValue = $position; + $newConditions[] = clone $newCondition; + } + } + elseif($condition->selectType == 'dept') + { + foreach($condition->depts as $dept) + { + $newCondition->conditionField = 'submitDepts'; + $newCondition->conditionValue = $dept; + $newConditions[] = clone $newCondition; + } + } + } + + $node->branches[$index]->conditions = $newConditions; + } + + if(isset($branch->nodes)) + { + foreach($branch->nodes as $branchID => $branchNode) + { + if($branchNode->type != 'branch') continue; + $node->branches[$index]->nodes[$branchID] = $this->transferCondition($branchNode); + + if(isset($branchNode->default->nodes)) + { + foreach($branchNode->default->nodes as $defaultID => $defaultNode) + { + if($defaultNode->type != 'branch') continue; + $node->branches[$index]->nodes[$branchID]->default->nodes[$defaultID] = $this->transferCondition($defaultNode); + } + } + } + } + } + + return $node; + } } diff --git a/module/user/js/batchcreate.ui.js b/module/user/js/batchcreate.ui.js index 859f30a8bd..83bf26da52 100644 --- a/module/user/js/batchcreate.ui.js +++ b/module/user/js/batchcreate.ui.js @@ -1,3 +1,21 @@ +window.renderRowData = function($row, index, row) +{ + $row.find('[data-name="companyItem"]').find('.picker-box').on('inited', function(e, info) + { + let $compnayPicker = info[0]; + if(index == 0) + { + let companyItems = info[0].options.items; + companyItems.pop(); + $compnayPicker.render({items: companyItems}); + } + else + { + $compnayPicker.$.setValue('ditto'); + } + }); +}; + /** * 根据用户类型切换控件的显示和隐藏。 * Toggle the display of controls according to the user type. diff --git a/module/user/model.php b/module/user/model.php index 4a2621392b..46b401286f 100644 --- a/module/user/model.php +++ b/module/user/model.php @@ -592,13 +592,19 @@ class userModel extends model $this->dao->begin(); $userIdList = array(); - foreach($users as $user) + $preCompany = 0; + foreach($users as $index => $user) { if(empty($user->account)) continue; $user->password = md5($user->password); - if($user->type == 'outside' && $user->new) $user->company = $this->createCompany($user->newCompany); + if($user->type == 'outside') + { + if($user->new) $user->company = $this->createCompany($user->newCompany); + if($this->post->company[$index] != 'ditto') $preCompany = $user->company; + if($this->post->company[$index] == 'ditto') $user->company = $preCompany; + } $this->dao->insert(TABLE_USER)->data($user, 'new,newCompany,group')->autoCheck()->exec(); if(dao::isError()) return $this->rollback(); diff --git a/module/user/test/lib/batchcreateuser.ui.class.php b/module/user/test/lib/batchcreateuser.ui.class.php new file mode 100755 index 0000000000..37220a27fd --- /dev/null +++ b/module/user/test/lib/batchcreateuser.ui.class.php @@ -0,0 +1,84 @@ +initForm('user', 'batchCreate', array(), 'appIframe-admin'); + $form->dom->account->setValue($user->account); + $form->dom->realname->setValue($user->realname); + $form->dom->passwordfield->setValue($user->passwordfield); + $form->dom->verifyPassword->setValue($user->verifyPassword); + $form->dom->btn($this->lang->save)->click(); + $form->wait(1); + + return $this->success('批量创建用户成功'); + } + + /** + * Check the page jump after batchcreate a empty account user. + * + * @param string $user + * @access public + * @return object + */ + public function batchCreateEmptyAccountUser($user) + { + $form = $this->initForm('user', 'batchCreate', array(), 'appIframe-admin'); + $form->dom->account->setValue($user->account); + $form->dom->realname->setValue($user->realname); + $form->dom->passwordfield->setValue($user->passwordfield); + $form->dom->verifyPassword->setValue($user->verifyPassword); + $form->dom->btn($this->lang->save)->click(); + $form->wait(1); + + return $this->failed('批量创建用户失败'); + } + + /** + * Check the page jump after batchcreate a empty realname user. + * + * @param string $user + * @access public + * @return object + */ + public function batchCreateEmptyRealnameUser($user) + { + $form = $this->initForm('user', 'batchCreate', array(), 'appIframe-admin'); + $form->dom->account->setValue($user->account); + $form->dom->realname->setValue($user->realname); + $form->dom->passwordfield->setValue($user->passwordfield); + $form->dom->verifyPassword->setValue($user->verifyPassword); + $form->dom->btn($this->lang->save)->click(); + $form->wait(1); + + return $this->failed('姓名不能为空'); + } + + /** + * Check the page jump after batchcreate a empty password user. + * + * @param string $user + * @access public + * @return object + */ + public function batchCreateEmptyPasswordUser($user) + { + $form = $this->initForm('user', 'batchCreate', array(), 'appIframe-admin'); + $form->dom->account->setValue($user->account); + $form->dom->realname->setValue($user->realname); + $form->dom->passwordfield->setValue($user->passwordfield); + $form->dom->verifyPassword->setValue($user->verifyPassword); + $form->dom->btn($this->lang->save)->click(); + $form->wait(1); + + return $this->failed('密码不能为空'); + } +} diff --git a/module/user/test/ui/batchcreateuser.php b/module/user/test/ui/batchcreateuser.php new file mode 100755 index 0000000000..0aa3effc8b --- /dev/null +++ b/module/user/test/ui/batchcreateuser.php @@ -0,0 +1,47 @@ +#!/usr/bin/env php +loadYaml('user', false, 2)->gen(10); +$tester = new batchCreateUserTester(); +$tester->login(); + +$user = new stdclass(); +$user->account = 'ljj'; +$user->realname = '李娟'; +$user->password = '123456'; +$user->verifyPassword = $config->uitest->defaultPassword; + +$user1 = new stdclass(); +$user1->account = ''; +$user1->realname = '李娟1'; +$user1->password = '123456'; +$user1->verifyPassword = $config->uitest->defaultPassword; + +$user2 = new stdclass(); +$user2->account = 'ljjj'; +$user2->realname = ''; +$user2->password = '123456'; +$user2->verifyPassword = $config->uitest->defaultPassword; + +$user3 = new stdclass(); +$user3->account = 'ljuanj'; +$user3->realname = '李娟2'; +$user3->password = ''; +$user3->verifyPassword = $config->uitest->defaultPassword; + +r($tester->batchCreateNormalUser($user)) && p('message') && e('批量建用户成功'); // 创建用户后的跳转链接检查 +r($tester->batchCreateEmptyAccountUser($user1)) && p('message') && e('批量创建用户失败'); // 创建用户后的跳转链接检查 +r($tester->batchCreateEmptyRealnameUser($user2)) && p('message') && e('姓名不能为空'); // 创建用户后的跳转链接检查 +r($tester->batchCreateEmptyPasswordUser($user3)) && p('message') && e('密码不能为空'); // 创建用户后的跳转链接检查 + +$tester->closeBrowser(); diff --git a/module/user/test/ui/page/batchcreate.php b/module/user/test/ui/page/batchcreate.php new file mode 100755 index 0000000000..56a7dec93f --- /dev/null +++ b/module/user/test/ui/page/batchcreate.php @@ -0,0 +1,14 @@ + "//*[@id='account_0']", + 'realname' => "//*[@id='realname_0']", + 'passwordfield' => "//*[@id='passwordfield_0']", + ); + $this->dom->xpath = array_merge($this->dom->xpath, $xpath); + } +} diff --git a/module/user/ui/batchcreate.html.php b/module/user/ui/batchcreate.html.php index d63b4c2ae6..dcd0479ed6 100644 --- a/module/user/ui/batchcreate.html.php +++ b/module/user/ui/batchcreate.html.php @@ -14,6 +14,8 @@ jsVar('roleGroup', $roleGroup); jsVar('passwordStrengthList', $lang->user->passwordStrengthList); h::jsCall('$.getLib', 'md5.js', array('root' => $this->app->getWebRoot() . 'js/')); +$companies += array('ditto' => $lang->user->ditto); + formBatchPanel ( set::title($lang->user->batchCreate), @@ -24,6 +26,7 @@ formBatchPanel on::keyup('[data-name="password"]', 'batchTogglePasswordStrength'), on::keyup('[name="verifyPassword"]', 'changePassword'), on::click('button[type=submit]', 'encryptPassword'), + set::onRenderRow(jsRaw('renderRowData')), to::titleSuffix ( div diff --git a/module/user/ui/login.html.php b/module/user/ui/login.html.php index cb413e9d0b..98e4739c01 100644 --- a/module/user/ui/login.html.php +++ b/module/user/ui/login.html.php @@ -171,7 +171,7 @@ div ( setID('notice'), setClass('alert secondary'), - div(setClass('content'), icon(setClass('text-secondary'), 'exclamation-sign'), $expiredCountTips) + div(setClass('content'), icon(setClass('text-secondary'), 'exclamation-sign'), html($expiredCountTips)) ) ) : null, empty($demoUsers) ? null : div diff --git a/module/webhook/tao.php b/module/webhook/tao.php index 398eb93689..cd089f57f0 100644 --- a/module/webhook/tao.php +++ b/module/webhook/tao.php @@ -122,7 +122,7 @@ class webhookTao extends webhookModel } elseif(isset($data->content)) { - $text = $data->content->text; + $text = empty($data->content->text) ? '' : (string)$data->content->text; $text = substr($text, 0, strpos($text, '(http')) ? substr($text, 0, strpos($text, '(http')) : zget($users, $data->user, $this->app->user->realname) . $this->lang->action->label->{$action->action} . $this->lang->action->objectTypes[$action->objectType] . "[#{$action->objectID}::{$object->$field}]"; } else diff --git a/module/zanode/model.php b/module/zanode/model.php index 6e2180ddca..a99c69d3e9 100644 --- a/module/zanode/model.php +++ b/module/zanode/model.php @@ -1,6 +1,6 @@ where('id')->eq($node->id) ->exec(); } + + if($oldNodeStatus != $node->status || !empty($osList[$node->osName])) + { + $this->dao->update(TABLE_ZAHOST) + ->beginIF($oldNodeStatus != $node->status)->set('status')->eq($node->status)->fi() + ->beginIF(!empty($osList[$node->osName]))->set('osName')->eq($osList[$node->osName])->fi() + ->where('id')->eq($node->id) + ->exec(); + } } return $nodeList; diff --git a/test/lib/webdriver/webdriver.class.php b/test/lib/webdriver/webdriver.class.php index a7438eee0a..79c60be3ce 100644 --- a/test/lib/webdriver/webdriver.class.php +++ b/test/lib/webdriver/webdriver.class.php @@ -999,7 +999,14 @@ class dom if($xpath != 'form') { - $this->driver->findElement(WebDriverBy::xpath("//*[@id='pick-pop-$pickerID']//span[@class='is-match-keys']"))->click(); + try + { + $this->driver->findElement(WebDriverBy::xpath("//*[@id='pick-pop-$pickerID']//span[@class='is-match-keys']"))->click(); + } + catch(Exception $xpathException) + { + $this->driver->findElement(WebDriverBy::xpath("//a[@title='$value']"))->click(); + } } else { @@ -1151,6 +1158,7 @@ class dom $name = $this->element->getAttribute('name'); if(!$name) return false; + if(strpos($name, '[') !== false) $name = '"' . $name . '"'; $this->driver->executeScript("return $('[name={$name}]').zui('datePicker').$.setValue('$value')"); return $this; } diff --git a/www/index.php b/www/index.php index ed4d8a1290..17ea22e9e8 100644 --- a/www/index.php +++ b/www/index.php @@ -83,7 +83,7 @@ try $common->checkPriv(); if(!$common->checkIframe()) helper::end(); - if(session_id() != $app->sessionID && strpos($_SERVER['HTTP_USER_AGENT'], 'xuanxuan') === false) helper::restartSession($app->sessionID); + if(session_id() != $app->sessionID && strpos($_SERVER['HTTP_USER_AGENT'], 'xuanxuan') === false && strpos($_SERVER['HTTP_USER_AGENT'], 'uni-app') === false) helper::restartSession($app->sessionID); $app->loadModule(); } diff --git a/www/js/zui3/icons/ZentaoIcon.eot b/www/js/zui3/icons/ZentaoIcon.eot index 147e09e6d1..a130b3d32a 100644 Binary files a/www/js/zui3/icons/ZentaoIcon.eot and b/www/js/zui3/icons/ZentaoIcon.eot differ diff --git a/www/js/zui3/icons/ZentaoIcon.svg b/www/js/zui3/icons/ZentaoIcon.svg index eacfc90790..531e85c574 100644 --- a/www/js/zui3/icons/ZentaoIcon.svg +++ b/www/js/zui3/icons/ZentaoIcon.svg @@ -302,6 +302,7 @@ + diff --git a/www/js/zui3/icons/ZentaoIcon.ttf b/www/js/zui3/icons/ZentaoIcon.ttf index 46eff7afe1..e3a9180e5e 100644 Binary files a/www/js/zui3/icons/ZentaoIcon.ttf and b/www/js/zui3/icons/ZentaoIcon.ttf differ diff --git a/www/js/zui3/icons/ZentaoIcon.woff b/www/js/zui3/icons/ZentaoIcon.woff index e5effc8647..0601d000a5 100644 Binary files a/www/js/zui3/icons/ZentaoIcon.woff and b/www/js/zui3/icons/ZentaoIcon.woff differ diff --git a/www/js/zui3/zin.js b/www/js/zui3/zin.js index c9ecaabcfd..d04b10f46d 100644 --- a/www/js/zui3/zin.js +++ b/www/js/zui3/zin.js @@ -190,6 +190,7 @@ function updatePageJS(data, _info, options) { if(window.onPageUnmount) window.onPageUnmount(); + $(document).trigger('pageunmount.app'); ['beforePageLoad', 'beforeRequestContent', 'onPageUnmount', 'beforePageUpdate', 'afterPageUpdate', 'onPageRender'].forEach(key => { @@ -549,6 +550,13 @@ }, []), renderOptions); if(!onlyZinDebug) updatePerfInfo(options, 'renderEnd', {perf: {clientCache: cacheHit ? cacheKey : null}}); }; + const callCallback = (name, args) => + { + let callback = options[name]; + if(!callback) return; + if(typeof callback === 'string') callback = zui.evalValue(callback); + return callback.apply(null, args); + }; const ajax = new zui.Ajax( { url: url + (url.includes('?') ? '&zin=1' : '?zin=1'), @@ -634,7 +642,7 @@ renderPageData(data, true); } $(document).trigger('pagerender.app'); - if(options.success) options.success(data); + callCallback('success', [data]); if(cacheKey) { const cacheTime = +response.headers.get('X-Zin-Cache-Time'); @@ -718,7 +726,7 @@ if(type === 'abort') return console.log('[ZIN] ', 'Abord fetch data from ' + url, {type, error}); if(DEBUG) console.error('[ZIN] ', 'Fetch data failed from ' + url, {type, error}); if(!data) zui.Messager.show('ZIN: Fetch data failed from ' + url); - if(options.error) options.error(data, error); + callCallback('error', [data, error]); }, complete: () => { @@ -726,7 +734,7 @@ if(onFinish) onFinish(); $(document).data('zinCache', null); if(options.loadingTarget !== false) toggleLoading(options.loadingTarget || target, false, options.loadingClass); - if(options.complete) options.complete(); + callCallback('complete', []); $(document).trigger('pageload.app'); const frameElement = window.frameElement; if(frameElement) @@ -1769,7 +1777,7 @@ return zui.Modal.confirm(data.confirm).then(confirmed => { if(confirmed) $(document).trigger('locate.zt', data.confirmed); - else $(document).trigger('locate.zt', data.cancelled); + else $(document).trigger('locate.zt', data.cancelled || data.canceled); }); } if(data.alert) diff --git a/www/js/zui3/zui.zentao.css b/www/js/zui3/zui.zentao.css index bb40e9ae70..f68e2cd380 100644 --- a/www/js/zui3/zui.zentao.css +++ b/www/js/zui3/zui.zentao.css @@ -1,2 +1,2 @@ -/*! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com*/*,:after,:before{border-color:rgb(var(--color-gray-200-rgb));border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:-apple-system,Noto Sans,Helvetica Neue,Helvetica,Nimbus Sans L,Arial,Liberation Sans,PingFang SC,Hiragino Sans GB,Noto Sans CJK SC,Source Han Sans SC,Source Han Sans CN,Microsoft YaHei,Wenquanyi Micro Hei,WenQuanYi Zen Hei,ST Heiti,SimHei,WenQuanYi Zen Hei Sharp,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:rgb(var(--color-gray-400-rgb));opacity:1}input::placeholder,textarea::placeholder{color:rgb(var(--color-gray-400-rgb));opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}:root{--screen-2xl:1720px;--color-gray-50:#fcfdfe;--color-gray-100:#eff1f3;--color-gray-200:#ebedf3;--color-gray-300:#d2d6e5;--color-gray-400:#b2b9c5;--color-gray-500:#64758b;--color-gray-600:#4a577f;--color-gray-700:#3d4667;--color-gray-800:#353d57;--color-gray-900:#30394a;--color-gray-950:#0b0f18;--color-gray-50-rgb:252,253,254;--color-gray-100-rgb:239,241,243;--color-gray-200-rgb:235,237,243;--color-gray-300-rgb:210,214,229;--color-gray-400-rgb:178,185,197;--color-gray-500-rgb:100,117,139;--color-gray-600-rgb:74,87,127;--color-gray-700-rgb:61,70,103;--color-gray-800-rgb:53,61,87;--color-gray-900-rgb:48,57,74;--color-gray-950-rgb:11,15,24;--color-primary-50:#eef6ff;--color-primary-100:#d9eaff;--color-primary-200:#bcdcff;--color-primary-300:#8ec6ff;--color-primary-400:#59a5ff;--color-primary-500:#3883fa;--color-primary-600:#2b67e5;--color-primary-700:#144ce1;--color-primary-800:#173db6;--color-primary-900:#19388f;--color-primary-950:#142457;--color-primary-50-rgb:238,246,255;--color-primary-100-rgb:217,234,255;--color-primary-200-rgb:188,220,255;--color-primary-300-rgb:142,198,255;--color-primary-400-rgb:89,165,255;--color-primary-500-rgb:56,131,250;--color-primary-600-rgb:43,103,229;--color-primary-700-rgb:20,76,225;--color-primary-800-rgb:23,61,182;--color-primary-900-rgb:25,56,143;--color-primary-950-rgb:20,36,87;--color-secondary-50:#e7f6ff;--color-secondary-100:#cdecff;--color-secondary-200:#9bd9ff;--color-secondary-300:#77cbff;--color-secondary-400:#60c2ff;--color-secondary-500:#37b2fe;--color-secondary-600:#18a6fd;--color-secondary-700:#1099ed;--color-secondary-800:#078ada;--color-secondary-900:#046bab;--color-secondary-950:#062a4b;--color-secondary-50-rgb:231,246,255;--color-secondary-100-rgb:205,236,255;--color-secondary-200-rgb:155,217,255;--color-secondary-300-rgb:119,203,255;--color-secondary-400-rgb:96,194,255;--color-secondary-500-rgb:55,178,254;--color-secondary-600-rgb:24,166,253;--color-secondary-700-rgb:16,153,237;--color-secondary-800-rgb:7,138,218;--color-secondary-900-rgb:4,107,171;--color-secondary-950-rgb:6,42,75;--color-success-50:#effaf6;--color-success-100:#d6f2e7;--color-success-200:#ade5ce;--color-success-300:#83d7b6;--color-success-400:#5aca9d;--color-success-500:#31bd85;--color-success-600:#2ba675;--color-success-700:#28996c;--color-success-800:#21805a;--color-success-900:#185e3b;--color-success-950:#0e3421;--color-success-50-rgb:239,250,246;--color-success-100-rgb:214,242,231;--color-success-200-rgb:173,229,206;--color-success-300-rgb:131,215,182;--color-success-400-rgb:90,202,157;--color-success-500-rgb:49,189,133;--color-success-600-rgb:43,166,117;--color-success-700-rgb:40,153,108;--color-success-800-rgb:33,128,90;--color-success-900-rgb:24,94,59;--color-success-950-rgb:14,52,33;--color-warning-50:#fff4ea;--color-warning-100:#ffecdb;--color-warning-200:#ffd2a9;--color-warning-300:#ffbc7e;--color-warning-400:#ffaf65;--color-warning-500:#ff9f46;--color-warning-600:#f38f19;--color-warning-700:#ed8307;--color-warning-800:#d17100;--color-warning-900:#af5f01;--color-warning-950:#441506;--color-warning-50-rgb:255,244,234;--color-warning-100-rgb:255,236,219;--color-warning-200-rgb:255,210,169;--color-warning-300-rgb:255,188,126;--color-warning-400-rgb:255,175,101;--color-warning-500-rgb:255,159,70;--color-warning-600-rgb:243,143,25;--color-warning-700-rgb:237,131,7;--color-warning-800-rgb:209,113,0;--color-warning-900-rgb:175,95,1;--color-warning-950-rgb:68,21,6;--color-danger-50:#ffebeb;--color-danger-100:#ffd5d5;--color-danger-200:#ffb1b1;--color-danger-300:#ff9292;--color-danger-400:#ff7c7c;--color-danger-500:#fc5959;--color-danger-600:#fb2b2b;--color-danger-700:#d91b1b;--color-danger-800:#ba1313;--color-danger-900:#a20606;--color-danger-950:#470808;--color-danger-50-rgb:255,235,235;--color-danger-100-rgb:255,213,213;--color-danger-200-rgb:255,177,177;--color-danger-300-rgb:255,146,146;--color-danger-400-rgb:255,124,124;--color-danger-500-rgb:252,89,89;--color-danger-600-rgb:251,43,43;--color-danger-700-rgb:217,27,27;--color-danger-800-rgb:186,19,19;--color-danger-900-rgb:162,6,6;--color-danger-950-rgb:71,8,8;--color-important-50:#fff8f5;--color-important-100:#ffe2d9;--color-important-200:#ffcebf;--color-important-300:#ffb29b;--color-important-400:#ff8c68;--color-important-500:#ff8058;--color-important-600:#ff6f42;--color-important-700:#ff4912;--color-important-800:#c83509;--color-important-900:#611700;--color-important-950:#440609;--color-important-50-rgb:255,248,245;--color-important-100-rgb:255,226,217;--color-important-200-rgb:255,206,191;--color-important-300-rgb:255,178,155;--color-important-400-rgb:255,140,104;--color-important-500-rgb:255,128,88;--color-important-600-rgb:255,111,66;--color-important-700-rgb:255,73,18;--color-important-800-rgb:200,53,9;--color-important-900-rgb:97,23,0;--color-important-950-rgb:68,6,9;--color-special-50:#efecfa;--color-special-100:#e4dff7;--color-special-200:#c4b6ff;--color-special-300:#a38cff;--color-special-400:#8a6ff5;--color-special-500:#8166ee;--color-special-600:#7055df;--color-special-700:#513baa;--color-special-800:#452da5;--color-special-900:#30216e;--color-special-950:#271560;--color-special-50-rgb:239,236,250;--color-special-100-rgb:228,223,247;--color-special-200-rgb:196,182,255;--color-special-300-rgb:163,140,255;--color-special-400-rgb:138,111,245;--color-special-500-rgb:129,102,238;--color-special-600-rgb:112,85,223;--color-special-700-rgb:81,59,170;--color-special-800-rgb:69,45,165;--color-special-900-rgb:48,33,110;--color-special-950-rgb:39,21,96;--color-inherit:inherit;--color-transparent:transparent;--color-current:currentColor;--color-black:#000;--color-white:#fff;--color-canvas:#fff;--color-canvas-light:#fff;--color-canvas-dark:#fcfdfe;--color-inverse:#000;--color-surface:#eff1f3;--color-surface-light:#fcfdfe;--color-surface-strong:#ebedf3;--color-fore:#30394a;--color-fore-in-light:#30394a;--color-fore-in-dark:#fff;--color-focus:#bcdcff;--color-link:#2b47a2;--color-link-hover:#144ce1;--color-link-visited:#6328a4;--color-link-active:#144ce1;--color-placeholder:#64758b;--color-border:#ebedf3;--color-border-strong:#d2d6e5;--color-border-light:#eff1f3;--color-black-rgb:0,0,0;--color-white-rgb:255,255,255;--color-canvas-rgb:255,255,255;--color-canvas-light-rgb:255,255,255;--color-canvas-dark-rgb:252,253,254;--color-inverse-rgb:0,0,0;--color-surface-rgb:239,241,243;--color-surface-light-rgb:252,253,254;--color-surface-strong-rgb:235,237,243;--color-fore-rgb:48,57,74;--color-fore-in-light-rgb:48,57,74;--color-fore-in-dark-rgb:255,255,255;--color-focus-rgb:188,220,255;--color-link-rgb:43,71,162;--color-link-hover-rgb:20,76,225;--color-link-visited-rgb:99,40,164;--color-link-active-rgb:20,76,225;--color-placeholder-rgb:100,117,139;--color-border-rgb:235,237,243;--color-border-strong-rgb:210,214,229;--color-border-light-rgb:239,241,243;--radius-none:0px;--radius-sm:0.0625rem;--radius:0.125rem;--radius-md:0.25rem;--radius-lg:0.375rem;--radius-xl:0.5rem;--radius-2xl:0.75rem;--radius-3xl:1rem;--radius-full:9999px;--shadow-sm:0 1px 2px 0 rgba(0,0,0,.04),0 0 2px -1px rgba(0,0,0,.01);--shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--shadow-md:0 2px 6px -1px rgba(0,0,0,.2),0 1px 4px -2px rgba(0,0,0,.15);--shadow-lg:0 5px 15px -3px rgba(0,0,0,.2),0 2px 6px -4px rgba(0,0,0,.15);--shadow-xl:0 10px 25px -5px rgba(0,0,0,.2),0 4px 10px -6px rgba(0,0,0,.15);--shadow-2xl:0 10px 50px -10px rgba(0,0,0,.4),0 8px 20px -10px rgba(0,0,0,.3);--shadow-inner:inset 0 2px 4px 0 rgba(0,0,0,.05);--shadow-none:none;--space:0.25rem;--font-size-article:0.875rem;--font-size-root:16px;--font-size-page-lineHeight:1.5384615385}.dark{--color-gray-50:#0b0f18;--color-gray-100:#30394a;--color-gray-200:#353d57;--color-gray-300:#3d4667;--color-gray-400:#4a577f;--color-gray-500:#64758b;--color-gray-600:#b2b9c5;--color-gray-700:#d2d6e5;--color-gray-800:#ebedf3;--color-gray-900:#eff1f3;--color-gray-950:#fcfdfe;--color-gray-50-rgb:11,15,24;--color-gray-100-rgb:48,57,74;--color-gray-200-rgb:53,61,87;--color-gray-300-rgb:61,70,103;--color-gray-400-rgb:74,87,127;--color-gray-500-rgb:100,117,139;--color-gray-600-rgb:178,185,197;--color-gray-700-rgb:210,214,229;--color-gray-800-rgb:235,237,243;--color-gray-900-rgb:239,241,243;--color-gray-950-rgb:252,253,254;--color-primary-50:#142457;--color-primary-100:#19388f;--color-primary-200:#173db6;--color-primary-300:#144ce1;--color-primary-400:#2b67e5;--color-primary-500:#3883fa;--color-primary-600:#59a5ff;--color-primary-700:#8ec6ff;--color-primary-800:#bcdcff;--color-primary-900:#d9eaff;--color-primary-950:#eef6ff;--color-primary-50-rgb:20,36,87;--color-primary-100-rgb:25,56,143;--color-primary-200-rgb:23,61,182;--color-primary-300-rgb:20,76,225;--color-primary-400-rgb:43,103,229;--color-primary-500-rgb:56,131,250;--color-primary-600-rgb:89,165,255;--color-primary-700-rgb:142,198,255;--color-primary-800-rgb:188,220,255;--color-primary-900-rgb:217,234,255;--color-primary-950-rgb:238,246,255;--color-secondary-50:#062a4b;--color-secondary-100:#046bab;--color-secondary-200:#078ada;--color-secondary-300:#1099ed;--color-secondary-400:#18a6fd;--color-secondary-500:#37b2fe;--color-secondary-600:#60c2ff;--color-secondary-700:#77cbff;--color-secondary-800:#9bd9ff;--color-secondary-900:#cdecff;--color-secondary-950:#e7f6ff;--color-secondary-50-rgb:6,42,75;--color-secondary-100-rgb:4,107,171;--color-secondary-200-rgb:7,138,218;--color-secondary-300-rgb:16,153,237;--color-secondary-400-rgb:24,166,253;--color-secondary-500-rgb:55,178,254;--color-secondary-600-rgb:96,194,255;--color-secondary-700-rgb:119,203,255;--color-secondary-800-rgb:155,217,255;--color-secondary-900-rgb:205,236,255;--color-secondary-950-rgb:231,246,255;--color-success-50:#0e3421;--color-success-100:#185e3b;--color-success-200:#21805a;--color-success-300:#28996c;--color-success-400:#2ba675;--color-success-500:#31bd85;--color-success-600:#5aca9d;--color-success-700:#83d7b6;--color-success-800:#ade5ce;--color-success-900:#d6f2e7;--color-success-950:#effaf6;--color-success-50-rgb:14,52,33;--color-success-100-rgb:24,94,59;--color-success-200-rgb:33,128,90;--color-success-300-rgb:40,153,108;--color-success-400-rgb:43,166,117;--color-success-500-rgb:49,189,133;--color-success-600-rgb:90,202,157;--color-success-700-rgb:131,215,182;--color-success-800-rgb:173,229,206;--color-success-900-rgb:214,242,231;--color-success-950-rgb:239,250,246;--color-warning-50:#441506;--color-warning-100:#af5f01;--color-warning-200:#d17100;--color-warning-300:#ed8307;--color-warning-400:#f38f19;--color-warning-500:#ff9f46;--color-warning-600:#ffaf65;--color-warning-700:#ffbc7e;--color-warning-800:#ffd2a9;--color-warning-900:#ffecdb;--color-warning-950:#fff4ea;--color-warning-50-rgb:68,21,6;--color-warning-100-rgb:175,95,1;--color-warning-200-rgb:209,113,0;--color-warning-300-rgb:237,131,7;--color-warning-400-rgb:243,143,25;--color-warning-500-rgb:255,159,70;--color-warning-600-rgb:255,175,101;--color-warning-700-rgb:255,188,126;--color-warning-800-rgb:255,210,169;--color-warning-900-rgb:255,236,219;--color-warning-950-rgb:255,244,234;--color-danger-50:#470808;--color-danger-100:#a20606;--color-danger-200:#ba1313;--color-danger-300:#d91b1b;--color-danger-400:#fb2b2b;--color-danger-500:#fc5959;--color-danger-600:#ff7c7c;--color-danger-700:#ff9292;--color-danger-800:#ffb1b1;--color-danger-900:#ffd5d5;--color-danger-950:#ffebeb;--color-danger-50-rgb:71,8,8;--color-danger-100-rgb:162,6,6;--color-danger-200-rgb:186,19,19;--color-danger-300-rgb:217,27,27;--color-danger-400-rgb:251,43,43;--color-danger-500-rgb:252,89,89;--color-danger-600-rgb:255,124,124;--color-danger-700-rgb:255,146,146;--color-danger-800-rgb:255,177,177;--color-danger-900-rgb:255,213,213;--color-danger-950-rgb:255,235,235;--color-important-50:#440609;--color-important-100:#611700;--color-important-200:#c83509;--color-important-300:#ff4912;--color-important-400:#ff6f42;--color-important-500:#ff8058;--color-important-600:#ff8c68;--color-important-700:#ffb29b;--color-important-800:#ffcebf;--color-important-900:#ffe2d9;--color-important-950:#fff8f5;--color-important-50-rgb:68,6,9;--color-important-100-rgb:97,23,0;--color-important-200-rgb:200,53,9;--color-important-300-rgb:255,73,18;--color-important-400-rgb:255,111,66;--color-important-500-rgb:255,128,88;--color-important-600-rgb:255,140,104;--color-important-700-rgb:255,178,155;--color-important-800-rgb:255,206,191;--color-important-900-rgb:255,226,217;--color-important-950-rgb:255,248,245;--color-special-50:#271560;--color-special-100:#30216e;--color-special-200:#452da5;--color-special-300:#513baa;--color-special-400:#7055df;--color-special-500:#8166ee;--color-special-600:#8a6ff5;--color-special-700:#a38cff;--color-special-800:#c4b6ff;--color-special-900:#e4dff7;--color-special-950:#efecfa;--color-special-50-rgb:39,21,96;--color-special-100-rgb:48,33,110;--color-special-200-rgb:69,45,165;--color-special-300-rgb:81,59,170;--color-special-400-rgb:112,85,223;--color-special-500-rgb:129,102,238;--color-special-600-rgb:138,111,245;--color-special-700-rgb:163,140,255;--color-special-800-rgb:196,182,255;--color-special-900-rgb:228,223,247;--color-special-950-rgb:239,236,250;--color-canvas:#0b0f18;--color-inverse:#fff;--color-surface:#353d57;--color-surface-light:#30394a;--color-surface-strong:#3d4667;--color-fore:#ebedf3;--color-focus:#173db6;--color-link:#59a5ff;--color-link-hover:#3883fa;--color-link-visited:#bcdcff;--color-link-active:#bcdcff;--color-placeholder:#4a577f;--color-border:#353d57;--color-border-strong:#3d4667;--color-border-light:#30394a;--color-canvas-rgb:11,15,24;--color-inverse-rgb:255,255,255;--color-surface-rgb:53,61,87;--color-surface-light-rgb:48,57,74;--color-surface-strong-rgb:61,70,103;--color-fore-rgb:235,237,243;--color-focus-rgb:23,61,182;--color-link-rgb:89,165,255;--color-link-hover-rgb:56,131,250;--color-link-visited-rgb:188,220,255;--color-link-active-rgb:188,220,255;--color-placeholder-rgb:74,87,127;--color-border-rgb:53,61,87;--color-border-strong-rgb:61,70,103;--color-border-light-rgb:48,57,74}.light-in-dark{--color-gray-50:#fcfdfe;--color-gray-100:#eff1f3;--color-gray-200:#ebedf3;--color-gray-300:#d2d6e5;--color-gray-400:#b2b9c5;--color-gray-500:#64758b;--color-gray-600:#4a577f;--color-gray-700:#3d4667;--color-gray-800:#353d57;--color-gray-900:#30394a;--color-gray-950:#0b0f18;--color-gray-50-rgb:252,253,254;--color-gray-100-rgb:239,241,243;--color-gray-200-rgb:235,237,243;--color-gray-300-rgb:210,214,229;--color-gray-400-rgb:178,185,197;--color-gray-500-rgb:100,117,139;--color-gray-600-rgb:74,87,127;--color-gray-700-rgb:61,70,103;--color-gray-800-rgb:53,61,87;--color-gray-900-rgb:48,57,74;--color-gray-950-rgb:11,15,24;--color-primary-50:#eef6ff;--color-primary-100:#d9eaff;--color-primary-200:#bcdcff;--color-primary-300:#8ec6ff;--color-primary-400:#59a5ff;--color-primary-500:#3883fa;--color-primary-600:#2b67e5;--color-primary-700:#144ce1;--color-primary-800:#173db6;--color-primary-900:#19388f;--color-primary-950:#142457;--color-primary-50-rgb:238,246,255;--color-primary-100-rgb:217,234,255;--color-primary-200-rgb:188,220,255;--color-primary-300-rgb:142,198,255;--color-primary-400-rgb:89,165,255;--color-primary-500-rgb:56,131,250;--color-primary-600-rgb:43,103,229;--color-primary-700-rgb:20,76,225;--color-primary-800-rgb:23,61,182;--color-primary-900-rgb:25,56,143;--color-primary-950-rgb:20,36,87;--color-secondary-50:#e7f6ff;--color-secondary-100:#cdecff;--color-secondary-200:#9bd9ff;--color-secondary-300:#77cbff;--color-secondary-400:#60c2ff;--color-secondary-500:#37b2fe;--color-secondary-600:#18a6fd;--color-secondary-700:#1099ed;--color-secondary-800:#078ada;--color-secondary-900:#046bab;--color-secondary-950:#062a4b;--color-secondary-50-rgb:231,246,255;--color-secondary-100-rgb:205,236,255;--color-secondary-200-rgb:155,217,255;--color-secondary-300-rgb:119,203,255;--color-secondary-400-rgb:96,194,255;--color-secondary-500-rgb:55,178,254;--color-secondary-600-rgb:24,166,253;--color-secondary-700-rgb:16,153,237;--color-secondary-800-rgb:7,138,218;--color-secondary-900-rgb:4,107,171;--color-secondary-950-rgb:6,42,75;--color-success-50:#effaf6;--color-success-100:#d6f2e7;--color-success-200:#ade5ce;--color-success-300:#83d7b6;--color-success-400:#5aca9d;--color-success-500:#31bd85;--color-success-600:#2ba675;--color-success-700:#28996c;--color-success-800:#21805a;--color-success-900:#185e3b;--color-success-950:#0e3421;--color-success-50-rgb:239,250,246;--color-success-100-rgb:214,242,231;--color-success-200-rgb:173,229,206;--color-success-300-rgb:131,215,182;--color-success-400-rgb:90,202,157;--color-success-500-rgb:49,189,133;--color-success-600-rgb:43,166,117;--color-success-700-rgb:40,153,108;--color-success-800-rgb:33,128,90;--color-success-900-rgb:24,94,59;--color-success-950-rgb:14,52,33;--color-warning-50:#fff4ea;--color-warning-100:#ffecdb;--color-warning-200:#ffd2a9;--color-warning-300:#ffbc7e;--color-warning-400:#ffaf65;--color-warning-500:#ff9f46;--color-warning-600:#f38f19;--color-warning-700:#ed8307;--color-warning-800:#d17100;--color-warning-900:#af5f01;--color-warning-950:#441506;--color-warning-50-rgb:255,244,234;--color-warning-100-rgb:255,236,219;--color-warning-200-rgb:255,210,169;--color-warning-300-rgb:255,188,126;--color-warning-400-rgb:255,175,101;--color-warning-500-rgb:255,159,70;--color-warning-600-rgb:243,143,25;--color-warning-700-rgb:237,131,7;--color-warning-800-rgb:209,113,0;--color-warning-900-rgb:175,95,1;--color-warning-950-rgb:68,21,6;--color-danger-50:#ffebeb;--color-danger-100:#ffd5d5;--color-danger-200:#ffb1b1;--color-danger-300:#ff9292;--color-danger-400:#ff7c7c;--color-danger-500:#fc5959;--color-danger-600:#fb2b2b;--color-danger-700:#d91b1b;--color-danger-800:#ba1313;--color-danger-900:#a20606;--color-danger-950:#470808;--color-danger-50-rgb:255,235,235;--color-danger-100-rgb:255,213,213;--color-danger-200-rgb:255,177,177;--color-danger-300-rgb:255,146,146;--color-danger-400-rgb:255,124,124;--color-danger-500-rgb:252,89,89;--color-danger-600-rgb:251,43,43;--color-danger-700-rgb:217,27,27;--color-danger-800-rgb:186,19,19;--color-danger-900-rgb:162,6,6;--color-danger-950-rgb:71,8,8;--color-important-50:#fff8f5;--color-important-100:#ffe2d9;--color-important-200:#ffcebf;--color-important-300:#ffb29b;--color-important-400:#ff8c68;--color-important-500:#ff8058;--color-important-600:#ff6f42;--color-important-700:#ff4912;--color-important-800:#c83509;--color-important-900:#611700;--color-important-950:#440609;--color-important-50-rgb:255,248,245;--color-important-100-rgb:255,226,217;--color-important-200-rgb:255,206,191;--color-important-300-rgb:255,178,155;--color-important-400-rgb:255,140,104;--color-important-500-rgb:255,128,88;--color-important-600-rgb:255,111,66;--color-important-700-rgb:255,73,18;--color-important-800-rgb:200,53,9;--color-important-900-rgb:97,23,0;--color-important-950-rgb:68,6,9;--color-special-50:#efecfa;--color-special-100:#e4dff7;--color-special-200:#c4b6ff;--color-special-300:#a38cff;--color-special-400:#8a6ff5;--color-special-500:#8166ee;--color-special-600:#7055df;--color-special-700:#513baa;--color-special-800:#452da5;--color-special-900:#30216e;--color-special-950:#271560;--color-special-50-rgb:239,236,250;--color-special-100-rgb:228,223,247;--color-special-200-rgb:196,182,255;--color-special-300-rgb:163,140,255;--color-special-400-rgb:138,111,245;--color-special-500-rgb:129,102,238;--color-special-600-rgb:112,85,223;--color-special-700-rgb:81,59,170;--color-special-800-rgb:69,45,165;--color-special-900-rgb:48,33,110;--color-special-950-rgb:39,21,96;--color-inherit:inherit;--color-transparent:transparent;--color-current:currentColor;--color-black:#000;--color-white:#fff;--color-canvas:#fff;--color-canvas-light:#fff;--color-canvas-dark:#fcfdfe;--color-inverse:#000;--color-surface:#eff1f3;--color-surface-light:#fcfdfe;--color-surface-strong:#ebedf3;--color-fore:#30394a;--color-fore-in-light:#30394a;--color-fore-in-dark:#fff;--color-focus:#bcdcff;--color-link:#2b47a2;--color-link-hover:#144ce1;--color-link-visited:#6328a4;--color-link-active:#144ce1;--color-placeholder:#64758b;--color-border:#ebedf3;--color-border-strong:#d2d6e5;--color-border-light:#eff1f3;--color-black-rgb:0,0,0;--color-white-rgb:255,255,255;--color-canvas-rgb:255,255,255;--color-canvas-light-rgb:255,255,255;--color-canvas-dark-rgb:252,253,254;--color-inverse-rgb:0,0,0;--color-surface-rgb:239,241,243;--color-surface-light-rgb:252,253,254;--color-surface-strong-rgb:235,237,243;--color-fore-rgb:48,57,74;--color-fore-in-light-rgb:48,57,74;--color-fore-in-dark-rgb:255,255,255;--color-focus-rgb:188,220,255;--color-link-rgb:43,71,162;--color-link-hover-rgb:20,76,225;--color-link-visited-rgb:99,40,164;--color-link-active-rgb:20,76,225;--color-placeholder-rgb:100,117,139;--color-border-rgb:235,237,243;--color-border-strong-rgb:210,214,229;--color-border-light-rgb:239,241,243}body{--btn-bg:var(--color-gray-50)}@media (prefers-color-scheme:dark){.dark-auto{--color-gray-50:#0b0f18;--color-gray-100:#30394a;--color-gray-200:#353d57;--color-gray-300:#3d4667;--color-gray-400:#4a577f;--color-gray-500:#64758b;--color-gray-600:#b2b9c5;--color-gray-700:#d2d6e5;--color-gray-800:#ebedf3;--color-gray-900:#eff1f3;--color-gray-950:#fcfdfe;--color-gray-50-rgb:11,15,24;--color-gray-100-rgb:48,57,74;--color-gray-200-rgb:53,61,87;--color-gray-300-rgb:61,70,103;--color-gray-400-rgb:74,87,127;--color-gray-500-rgb:100,117,139;--color-gray-600-rgb:178,185,197;--color-gray-700-rgb:210,214,229;--color-gray-800-rgb:235,237,243;--color-gray-900-rgb:239,241,243;--color-gray-950-rgb:252,253,254;--color-primary-50:#142457;--color-primary-100:#19388f;--color-primary-200:#173db6;--color-primary-300:#144ce1;--color-primary-400:#2b67e5;--color-primary-500:#3883fa;--color-primary-600:#59a5ff;--color-primary-700:#8ec6ff;--color-primary-800:#bcdcff;--color-primary-900:#d9eaff;--color-primary-950:#eef6ff;--color-primary-50-rgb:20,36,87;--color-primary-100-rgb:25,56,143;--color-primary-200-rgb:23,61,182;--color-primary-300-rgb:20,76,225;--color-primary-400-rgb:43,103,229;--color-primary-500-rgb:56,131,250;--color-primary-600-rgb:89,165,255;--color-primary-700-rgb:142,198,255;--color-primary-800-rgb:188,220,255;--color-primary-900-rgb:217,234,255;--color-primary-950-rgb:238,246,255;--color-secondary-50:#062a4b;--color-secondary-100:#046bab;--color-secondary-200:#078ada;--color-secondary-300:#1099ed;--color-secondary-400:#18a6fd;--color-secondary-500:#37b2fe;--color-secondary-600:#60c2ff;--color-secondary-700:#77cbff;--color-secondary-800:#9bd9ff;--color-secondary-900:#cdecff;--color-secondary-950:#e7f6ff;--color-secondary-50-rgb:6,42,75;--color-secondary-100-rgb:4,107,171;--color-secondary-200-rgb:7,138,218;--color-secondary-300-rgb:16,153,237;--color-secondary-400-rgb:24,166,253;--color-secondary-500-rgb:55,178,254;--color-secondary-600-rgb:96,194,255;--color-secondary-700-rgb:119,203,255;--color-secondary-800-rgb:155,217,255;--color-secondary-900-rgb:205,236,255;--color-secondary-950-rgb:231,246,255;--color-success-50:#0e3421;--color-success-100:#185e3b;--color-success-200:#21805a;--color-success-300:#28996c;--color-success-400:#2ba675;--color-success-500:#31bd85;--color-success-600:#5aca9d;--color-success-700:#83d7b6;--color-success-800:#ade5ce;--color-success-900:#d6f2e7;--color-success-950:#effaf6;--color-success-50-rgb:14,52,33;--color-success-100-rgb:24,94,59;--color-success-200-rgb:33,128,90;--color-success-300-rgb:40,153,108;--color-success-400-rgb:43,166,117;--color-success-500-rgb:49,189,133;--color-success-600-rgb:90,202,157;--color-success-700-rgb:131,215,182;--color-success-800-rgb:173,229,206;--color-success-900-rgb:214,242,231;--color-success-950-rgb:239,250,246;--color-warning-50:#441506;--color-warning-100:#af5f01;--color-warning-200:#d17100;--color-warning-300:#ed8307;--color-warning-400:#f38f19;--color-warning-500:#ff9f46;--color-warning-600:#ffaf65;--color-warning-700:#ffbc7e;--color-warning-800:#ffd2a9;--color-warning-900:#ffecdb;--color-warning-950:#fff4ea;--color-warning-50-rgb:68,21,6;--color-warning-100-rgb:175,95,1;--color-warning-200-rgb:209,113,0;--color-warning-300-rgb:237,131,7;--color-warning-400-rgb:243,143,25;--color-warning-500-rgb:255,159,70;--color-warning-600-rgb:255,175,101;--color-warning-700-rgb:255,188,126;--color-warning-800-rgb:255,210,169;--color-warning-900-rgb:255,236,219;--color-warning-950-rgb:255,244,234;--color-danger-50:#470808;--color-danger-100:#a20606;--color-danger-200:#ba1313;--color-danger-300:#d91b1b;--color-danger-400:#fb2b2b;--color-danger-500:#fc5959;--color-danger-600:#ff7c7c;--color-danger-700:#ff9292;--color-danger-800:#ffb1b1;--color-danger-900:#ffd5d5;--color-danger-950:#ffebeb;--color-danger-50-rgb:71,8,8;--color-danger-100-rgb:162,6,6;--color-danger-200-rgb:186,19,19;--color-danger-300-rgb:217,27,27;--color-danger-400-rgb:251,43,43;--color-danger-500-rgb:252,89,89;--color-danger-600-rgb:255,124,124;--color-danger-700-rgb:255,146,146;--color-danger-800-rgb:255,177,177;--color-danger-900-rgb:255,213,213;--color-danger-950-rgb:255,235,235;--color-important-50:#440609;--color-important-100:#611700;--color-important-200:#c83509;--color-important-300:#ff4912;--color-important-400:#ff6f42;--color-important-500:#ff8058;--color-important-600:#ff8c68;--color-important-700:#ffb29b;--color-important-800:#ffcebf;--color-important-900:#ffe2d9;--color-important-950:#fff8f5;--color-important-50-rgb:68,6,9;--color-important-100-rgb:97,23,0;--color-important-200-rgb:200,53,9;--color-important-300-rgb:255,73,18;--color-important-400-rgb:255,111,66;--color-important-500-rgb:255,128,88;--color-important-600-rgb:255,140,104;--color-important-700-rgb:255,178,155;--color-important-800-rgb:255,206,191;--color-important-900-rgb:255,226,217;--color-important-950-rgb:255,248,245;--color-special-50:#271560;--color-special-100:#30216e;--color-special-200:#452da5;--color-special-300:#513baa;--color-special-400:#7055df;--color-special-500:#8166ee;--color-special-600:#8a6ff5;--color-special-700:#a38cff;--color-special-800:#c4b6ff;--color-special-900:#e4dff7;--color-special-950:#efecfa;--color-special-50-rgb:39,21,96;--color-special-100-rgb:48,33,110;--color-special-200-rgb:69,45,165;--color-special-300-rgb:81,59,170;--color-special-400-rgb:112,85,223;--color-special-500-rgb:129,102,238;--color-special-600-rgb:138,111,245;--color-special-700-rgb:163,140,255;--color-special-800-rgb:196,182,255;--color-special-900-rgb:228,223,247;--color-special-950-rgb:239,236,250;--color-canvas:#0b0f18;--color-inverse:#fff;--color-surface:#353d57;--color-surface-light:#30394a;--color-surface-strong:#3d4667;--color-fore:#ebedf3;--color-focus:#173db6;--color-link:#59a5ff;--color-link-hover:#3883fa;--color-link-visited:#bcdcff;--color-link-active:#bcdcff;--color-placeholder:#4a577f;--color-border:#353d57;--color-border-strong:#3d4667;--color-border-light:#30394a;--color-canvas-rgb:11,15,24;--color-inverse-rgb:255,255,255;--color-surface-rgb:53,61,87;--color-surface-light-rgb:48,57,74;--color-surface-strong-rgb:61,70,103;--color-fore-rgb:235,237,243;--color-focus-rgb:23,61,182;--color-link-rgb:89,165,255;--color-link-hover-rgb:56,131,250;--color-link-visited-rgb:188,220,255;--color-link-active-rgb:188,220,255;--color-placeholder-rgb:74,87,127;--color-border-rgb:53,61,87;--color-border-strong-rgb:61,70,103;--color-border-light-rgb:48,57,74}}*,:after,:before{--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity))}:focus-visible{outline-color:transparent}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }html{font-size:var(--font-size-root)}body{--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));font-size:.8125rem;line-height:1.5384615385}.dark,body{--tw-text-opacity:1;color:rgba(var(--color-fore-rgb),var(--tw-text-opacity))}:root{--font-h1-size:2.25rem;--article-p-space:0.5em;--article-heading-space:0.5em;--article-hr-space:1.5em;--article-cell-padding:0.25rem 0.75rem}a,a.disabled:hover{--tw-text-opacity:1;color:rgba(var(--color-link-rgb),var(--tw-text-opacity))}a:hover{--tw-text-opacity:1;color:rgba(var(--color-link-hover-rgb),var(--tw-text-opacity))}a:focus-visible{outline-color:rgb(var(--color-focus-rgb));outline-offset:2px;outline-width:2px}.code,code{background-color:rgba(var(--color-important-500-rgb),.1);border-radius:var(--radius);padding-left:.25rem;padding-right:.25rem;--tw-text-opacity:1;color:rgba(var(--color-important-500-rgb),var(--tw-text-opacity))}pre{font-size:.75rem;line-height:1rem;overflow:auto;padding:.5rem}pre code{background-color:transparent;color:inherit;padding:0}samp,var{font-weight:700}.blockquote,blockquote{border-left-width:4px;font-size:.875rem;line-height:1.3rem;padding-left:.75rem;text-wrap:balance}.blockquote figcaption,blockquote figcaption{font-size:.75rem;line-height:1rem;--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-weight:700;line-height:1.5;text-wrap:balance}.h1,h1{font-size:1.75rem}.h2,h2{font-size:1.5rem}.h3,h3{font-size:1.25rem}.h4,h4{font-size:1rem}.h5,h5{font-size:.875rem}.h6,h6{font-size:.75rem}ul{list-style-type:disc}ol,ul{padding-left:1.5em}ol{list-style-type:decimal}dt{font-weight:700}dd{padding-left:2em}.article{color:var(--font-size-article);-webkit-hyphens:auto;hyphens:auto;overflow-wrap:break-word}.article h1,.article h2,.article h3,.article h4{margin-bottom:var(--article-heading-space);margin-top:var(--article-heading-space)}.article audio,.article blockquote,.article figure,.article img,.article ol,.article p,.article table,.article ul,.article video{margin-bottom:var(--article-p-space);margin-top:var(--article-p-space)}.article p{min-height:1em}.article p:empty:before{content:" ";display:inline-block}.article hr{margin-bottom:var(--article-hr-space);margin-top:var(--article-hr-space)}.article table{border-width:1px}.article td,.article th{border-bottom-width:1px;padding:var(--article-cell-padding)}.article thead{background-color:var(--table-head-bg,var(--color-gray-100))}.article ol *,.article table *,.article ul *{margin-bottom:0;margin-top:0}.article pre{border-radius:var(--radius);border-width:1px;--tw-bg-opacity:1;background-color:rgba(var(--color-gray-50-rgb),var(--tw-bg-opacity))}:root{--alert-bg:var(--color-surface)}.alert{align-items:center;background-color:var(--alert-bg);border-radius:var(--radius);display:flex;gap:.75rem;padding:.5rem .75rem}.alert-content{display:flex;flex:1 1 auto;flex-direction:column;gap:1rem}.alert-close{margin-bottom:-.375rem;margin-right:-.5rem;margin-top:-.375rem}.alert-link{color:inherit;font-weight:700}.alert-link:hover{color:inherit;text-decoration-line:underline}.alert-heading{font-weight:700;margin:0}.alert-text{flex:1 1 auto;white-space:pre-line}.alert-actions{align-items:center;display:flex;gap:.5rem}:root{--breadcrumb-divider:"/";--breadcrumb-divider-color:var(--color-gray-500);--breadcrumb-color-active:var(--color-gray-500)}.breadcrumb{display:flex;gap:1.25rem;list-style-type:none;padding-left:0}.breadcrumb>li{position:relative}.breadcrumb>li+li:before{color:var(--breadcrumb-divider-color);content:var(--breadcrumb-divider);display:block;left:-1.25rem;position:absolute;text-align:center;width:1.25rem}.breadcrumb>.active{color:var(--breadcrumb-color-active)}:root{--btn-radius:var(--radius);--btn-bg:var(--color-surface);--btn-border-color:var(--color-gray-300);--btn-height:2rem}.btn{align-items:center;background-color:var(--btn-bg);border-radius:var(--btn-radius);display:inline-flex;gap:.375rem;height:var(--btn-height);justify-content:center;padding-left:.75rem;padding-right:.75rem;white-space:nowrap;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:var(--btn-border-color)}.btn,.btn-default:hover{color:inherit}.btn-link{--tw-text-opacity:1;color:rgba(var(--color-link-rgb),var(--tw-text-opacity));text-decoration-line:underline;text-underline-offset:2px;--tw-ring-color:transparent}.btn-link:visited{color:rgba(var(--color-link-visited-rgb),var(--tw-text-opacity))}.btn-link:hover{--tw-text-opacity:1;color:rgba(var(--color-link-hover-rgb),var(--tw-text-opacity))}.btn-link{--btn-bg:transparent}.btn.size-xs,.size-xs>.btn{padding-left:.25rem;padding-right:.25rem;--btn-height:1.25rem}.btn.size-sm,.btn.size-xs,.size-sm>.btn,.size-xs>.btn{font-size:.75rem;line-height:1rem}.btn.size-sm,.size-sm>.btn{--btn-height:1.5rem}.btn.size-md,.btn.size-sm,.size-md>.btn,.size-sm>.btn{padding-left:.5rem;padding-right:.5rem}.btn.size-md,.size-md>.btn{--btn-height:1.75rem}.btn.size-lg,.size-lg>.btn{font-size:1rem;line-height:1.5rem;padding-left:1rem;padding-right:1rem;--btn-height:2.5rem}.btn.size-xl,.size-xl>.btn{font-size:1.125rem;line-height:1.75rem;padding-left:1.25rem;padding-right:1.25rem;--btn-height:3rem}.btn.square{aspect-ratio:1/1;gap:.125rem;padding-left:0;padding-right:0}@supports not (aspect-ratio:1/1){.btn.square{width:var(--btn-height)}}.btn.btn-caret{padding-left:.25rem;padding-right:.25rem}.btn.btn-wide{min-width:80px}:root{--checkbox-size:12px;--checkbox-color:var(--color-primary-500)}.checkbox,.radio{align-items:center;cursor:pointer;display:flex;gap:.375rem;min-height:20px}.checkbox>input[type=checkbox],.radio>input[type=radio]{accent-color:rgb(var(--color-primary-500-rgb));border-radius:var(--radius-lg)}.checkbox-primary,.radio-primary{display:flex;gap:.375rem;min-height:20px;position:relative}.checkbox-primary>input[type=checkbox],.radio-primary>input[type=radio]{bottom:0;left:0;opacity:0;position:absolute;right:0;top:0}.checkbox-primary>label,.radio-primary>label{cursor:pointer;padding-left:calc(var(--checkbox-size) + 8px);position:relative}.checkbox-primary>label:after,.checkbox-primary>label:before,.radio-primary>label:after,.radio-primary>label:before{display:block;height:var(--checkbox-size);left:0;position:absolute;transition-duration:.15s;transition-property:transform,background-color,color,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--checkbox-size);--tw-content:"";content:var(--tw-content);top:calc((20px - var(--checkbox-size))/2)}.checkbox-primary>label:before{border-radius:var(--radius-sm);border-width:1px;--tw-border-opacity:1;border-color:rgba(var(--color-gray-400-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.checkbox-primary>label:after{--tw-scale-x:.9;--tw-scale-y:.9;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0ibTkgMjAuNDItNi4yMS02LjIxIDIuODMtMi44M0w5IDE0Ljc3bDkuODgtOS44OSAyLjgzIDIuODN6Ii8+PC9zdmc+");transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.radio-primary>label:before{border-radius:var(--radius-full);border-width:1px;--tw-border-opacity:1;border-color:rgba(var(--color-gray-400-rgb),var(--tw-border-opacity))}.radio-primary>label:after{background-color:var(--checkbox-color);border-radius:var(--radius-full);opacity:0;--checkbox-radio-size:calc(var(--checkbox-size)*2/3);height:var(--checkbox-radio-size);left:calc((var(--checkbox-size) - var(--checkbox-radio-size))/2);top:calc((20px - var(--checkbox-radio-size))/2);width:var(--checkbox-radio-size)}.checkbox-primary>label:hover:before,.radio-primary>label:hover:before{border-color:var(--checkbox-color)}.checkbox-primary.focus>label:before,.checkbox-primary>input[type=checkbox]:focus+label:before,.radio-primary.focus>label:before,.radio-primary>input[type=radio]:focus+label:before{border-color:var(--checkbox-color);outline-color:rgb(var(--color-focus-rgb));outline-style:solid;outline-width:2px}.checkbox-primary.checked>label:before,.checkbox-primary>input[type=checkbox]:checked+label:before{background-color:var(--checkbox-color);border-color:var(--checkbox-color)}.radio-primary.checked>label:before,.radio-primary>input[type=radio]:checked+label:before{border-color:var(--checkbox-color)}.checkbox-primary.checked>label:after,.checkbox-primary>input[type=checkbox]:checked+label:after,.radio-primary.checked>label:after,.radio-primary>input[type=radio]:checked+label:after{opacity:1}.checkbox-primary.disabled>label,.checkbox-primary>input[type=checkbox]:disabled+label,.radio-primary.disabled>label,.radio-primary>input[type=radio]:disabled+label{cursor:not-allowed;opacity:.7;pointer-events:none;--tw-grayscale:grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.checkbox-primary.indeterminate>label:after,.checkbox-primary>input[type=checkbox]:indeterminate+label:after{--tw-border-opacity:1;background-color:var(--checkbox-color);background-image:none;border-color:rgba(var(--color-canvas-rgb),var(--tw-border-opacity));border-width:calc((var(--checkbox-size) - 2px)/2) 2px;opacity:1}.check-list{align-items:flex-start;flex-direction:column;gap:.5rem}.check-list,.check-list-inline{display:flex;padding-bottom:.375rem;padding-top:.375rem}.check-list-inline{align-items:center;-moz-column-gap:1rem;column-gap:1rem;flex-direction:row;flex-wrap:wrap;row-gap:.5rem}.switch{display:flex;gap:.375rem;min-height:20px;padding-left:2.5rem;position:relative}.switch>input{bottom:0;left:0;opacity:0;position:absolute;right:0;top:0}.switch>label:after,.switch>label:before{border-radius:var(--radius-full);border-width:1px;height:1rem;left:0;position:absolute;top:.125rem;width:2rem;--tw-border-opacity:1;border-color:rgba(var(--color-border-strong-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity));transition-duration:.3s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);--tw-content:"";content:var(--tw-content)}.switch>label:before{cursor:pointer;--tw-shadow:inset 0 2px 4px 0 rgba(0,0,0,.05);--tw-shadow-colored:inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.switch>label:hover:before{--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity))}.switch>label:after{height:.75rem;left:.125rem;top:.25rem;width:.75rem;--tw-border-opacity:1;border-color:rgba(var(--color-canvas-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));--tw-shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 3px 8px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.switch>input:checked+label:before{background-color:var(--checkbox-color);border-color:rgba(var(--color-inverse-rgb),.2)}.switch>input:checked+label:after{color:var(--checkbox-color);left:18px}.switch.focus>label:before,.switch>input:focus+label:before{border-color:var(--checkbox-color);outline-color:rgb(var(--color-focus-rgb));outline-style:solid;outline-width:2px}.switch.disabled>label:before,.switch>input:disabled+label:before{cursor:not-allowed;opacity:.7;pointer-events:none;--tw-grayscale:grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}:root{--form-control-radius:var(--radius);--form-control-border:var(--color-border);--form-control-focus:var(--color-primary-500);--form-control-disabled:var(--color-surface);--form-control-readonly:var(--color-surface)}.dark{--form-control-border:var(--color-border-strong)}input::-moz-placeholder,textarea::-moz-placeholder{--tw-placeholder-opacity:1;color:rgba(var(--color-placeholder-rgb),var(--tw-placeholder-opacity))}input::placeholder,textarea::placeholder{--tw-placeholder-opacity:1;color:rgba(var(--color-placeholder-rgb),var(--tw-placeholder-opacity))}.form-control{border-radius:var(--form-control-radius);display:block;height:2rem;width:100%;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));outline-color:transparent;outline-style:solid;outline-width:1px;padding:.25rem .5rem;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:var(--form-control-border);transition-duration:.15s;transition-property:outline,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1)}.form-control.focus,.form-control:focus{outline-color:var(--form-control-focus);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:rgba(var(--color-focus-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.3}.form-control.disabled,.form-control[disabled]{background-color:var(--form-control-disabled);opacity:1}.form-control.readonly,.form-control[readonly]{background-color:var(--form-control-readonly)}select{-webkit-appearance:none;background-image:url("data:image/gif;base64,R0lGODlhBwAEAIAAAMvQ2////yH5BAEAAAEALAAAAAAHAAQAAAIIhA+BGWoNWSgAOw==");background-position:right 8px top 50%;background-repeat:no-repeat;background-size:8px auto;padding-right:1rem}select.form-control[multiple]{background-image:none}textarea.form-control{height:auto;min-height:32px}input[type=file].form-control{padding:.125rem}input[type=file].form-control::-webkit-file-upload-button{-webkit-appearance:none;appearance:none;border-radius:inherit;border-style:none;cursor:pointer;height:1.75rem;padding-left:.5rem;padding-right:.5rem}input[type=file].form-control::-webkit-file-upload-button:hover{background-color:rgba(var(--color-black-rgb),.1)}.has-error .form-control,.has-error.form-control{--tw-ring-color:rgba(var(--color-danger-500-rgb),0.6)}.has-error .form-control.focus,.has-error .form-control:focus,.has-error.form-control.focus,.has-error.form-control:focus{outline-color:rgb(var(--color-danger-500-rgb));--tw-ring-color:rgba(var(--color-danger-500-rgb),0.1)}.has-warning .form-control,.has-warning.form-control{--tw-ring-color:rgba(var(--color-warning-500-rgb),0.6)}.has-warning .form-control.focus,.has-warning .form-control:focus,.has-warning.form-control.focus,.has-warning.form-control:focus{outline-color:rgb(var(--color-warning-500-rgb));--tw-ring-color:rgba(var(--color-warning-500-rgb),0.2)}.has-success .form-control,.has-success.form-control{--tw-ring-color:rgba(var(--color-success-500-rgb),0.6)}.has-success .form-control.focus,.has-success .form-control:focus,.has-success.form-control.focus,.has-success.form-control:focus{outline-color:rgb(var(--color-success-500-rgb));--tw-ring-color:rgba(var(--color-success-500-rgb),0.2)}.form-control.size-sm,.size-sm>.form-control{font-size:.75rem;height:1.5rem;line-height:1rem;padding-left:.5rem;padding-right:.5rem}.form-control.size-md,.size-md>.form-control{height:1.75rem;padding-left:.5rem;padding-right:.5rem}.form-control.size-lg,.size-lg>.form-control{font-size:1rem;height:2.5rem;line-height:1.5rem;padding-left:.75rem;padding-right:.75rem}:root{--input-group-addon-bg:var(--color-gray-50)}.input-group{align-items:stretch;display:flex}.input-group>.form-control,.input-group>.input-control{flex:1 1 auto;z-index:1}.input-group-addon{align-items:center;background-color:var(--input-group-addon-bg);border-radius:var(--form-control-radius);display:flex;flex:none;height:2rem;padding-left:.5rem;padding-right:.5rem;z-index:0;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:var(--form-control-border)}.size-sm>.input-group-addon{font-size:.75rem;height:1.5rem;line-height:1rem;padding-left:.5rem;padding-right:.5rem}.size-lg>.input-group-addon{font-size:1rem;height:2.5rem;line-height:1.5rem;padding-left:.75rem;padding-right:.75rem}.input-group-control{flex:1 1 auto}.input-group>*+*,.input-group>*+* .form-control{border-bottom-left-radius:var(--radius-none);border-top-left-radius:var(--radius-none)}.input-group>:not(:last-child),.input-group>:not(:last-child) .form-control{border-bottom-right-radius:var(--radius-none);border-top-right-radius:var(--radius-none)}.input-group .btn:focus-visible,.input-group .form-control:focus{z-index:2}:root{--form-tip-color:var(--color-gray-400);--form-label-color:var(--color-gray-700);--form-horz-label-width:6rem;--form-grid-gap-x:1.25rem;--form-grid-gap-y:.75rem}.form{flex-direction:column;gap:1rem}.form,.form-label{display:flex}.form-label{align-items:center;color:var(--form-label-color);flex-direction:row;gap:.25rem;height:2rem;overflow:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap}.form-label.required:after{content:"*";display:inline-block;margin-left:.25rem;--tw-translate-y:0.125rem;--tw-scale-x:1.25;--tw-scale-y:1.25;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-text-opacity:1;color:rgba(var(--color-danger-500-rgb),var(--tw-text-opacity))}.no-required .form-label.required:after{display:none}.form-label-actions{flex:1 1 auto;justify-content:flex-end}.form-label>.checkbox-primary{margin-left:.75rem}.form-tip{color:var(--form-tip-color);display:block;margin-top:.25rem}.has-error .form-tip{--tw-text-opacity:1;color:rgba(var(--color-danger-500-rgb),var(--tw-text-opacity))}.has-warning .form-tip{--tw-text-opacity:1;color:rgba(var(--color-warning-500-rgb),var(--tw-text-opacity))}.has-success .form-tip{--tw-text-opacity:1;color:rgba(var(--color-success-500-rgb),var(--tw-text-opacity))}.form-actions{display:flex;flex-direction:row;gap:1rem;justify-content:center}.form-grid{--form-grid-gap-x-half:calc(var(--form-grid-gap-x)/2);--form-grid-gap-y-half:calc(var(--form-grid-gap-y)/2);align-items:flex-start;flex-direction:row;flex-wrap:wrap;gap:0}.form-grid .form-group{padding:var(--form-grid-gap-y-half) var(--form-grid-gap-x-half)}.form-grid .form-label.required:after{margin-left:.25rem;order:-9999}.form-grid .form-label .checkbox{margin-left:.75rem;margin-right:.75rem}.form-grid .has-wrap-after:after{display:block;width:100%;--tw-content:"";content:var(--tw-content)}.form-grid-wrap{width:100%}.form-grid-row{display:flex;flex-direction:row;padding-bottom:var(--form-grid-gap-y-half);padding-top:var(--form-grid-gap-y-half);position:relative;width:100%}.form-grid-col{padding-left:var(--form-grid-gap-x-half);padding-right:var(--form-grid-gap-x-half)}.form-grid .form-actions{gap:1.5rem;justify-content:center;padding:var(--form-grid-gap-y) var(--form-grid-gap-x-half);width:100%}.form-grid .form-actions.is-pinned{border-top-width:1px}.form-horz .form-row{align-items:flex-start;display:flex;flex-direction:row}.form-horz .form-group{align-items:flex-start;display:flex;flex:none;flex-direction:row;flex-wrap:wrap;min-height:32px;min-width:0;padding-left:var(--form-horz-label-width);position:relative}.form-horz .form-row .form-group{flex-grow:1}.form-horz .form-group[class*=" w-"],.form-horz .form-group[class^=w-]{flex:none}.form-horz .form-group>*{min-width:0}.form-horz .form-group.no-label{padding-left:0}.form-horz .form-group.no-label>.check-list-inline{padding-left:1rem;padding-right:1rem}.form-horz .form-label{justify-content:flex-end;left:0;padding-left:1rem;padding-right:.5rem;position:absolute;top:0;width:var(--form-horz-label-width)}.form-horz .form-label.required:after{margin-left:0;order:-9999}.form-horz .form-tip{width:100%}.form-horz .form-tip,.form-horz .input-control,.form-horz .input-group{flex:1 1 auto}.form .form-group-wrapper,.form-horz .form-group-wrapper{flex:1 1 auto;min-width:0;width:100%;z-index:1}.form .form-group-wrapper.flex-none,.form-horz .form-group-wrapper.flex-none{flex:none}.form fieldset>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.form fieldset{padding-bottom:1rem;padding-left:1rem}.form-grid fieldset>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.form legend{border-bottom-width:1px;display:flex;justify-content:flex-start;margin-left:-1rem;width:100%;--tw-border-opacity:1;border-bottom-color:rgba(var(--color-primary-100-rgb),var(--tw-border-opacity));width:calc(100% + 1rem)}.form-title{align-items:center;background-color:rgba(var(--color-primary-50-rgb),.5);display:flex;font-weight:700;height:1.75rem;padding-left:.75rem;padding-right:.75rem}:root{--input-control-fix-width-sm:2rem;--input-control-fix-width:4.375rem;--input-control-fix-width-lg:6.75rem}.input-control{position:relative;--input-control-prefix:8px;--input-control-suffix:8px}.input-control-prefix,.input-control-suffix{align-items:center;display:flex;height:100%;left:0;opacity:.5;padding-left:.5rem;padding-right:.5rem;position:absolute;top:0;white-space:nowrap;width:var(--input-control-prefix);z-index:1}.input-control-suffix{justify-content:flex-end;left:auto;right:0;width:var(--input-control-suffix)}.form-control:focus+.input-control-prefix,.form-control:focus+.input-control-suffix{opacity:1}.input-control>.form-control{padding-left:var(--input-control-prefix);padding-right:var(--input-control-suffix)}.input-control>select.form-control{background-position-x:calc(100% - var(--input-control-suffix) - 8px);padding-right:calc(var(--input-control-suffix) + 20px)}.has-prefix{--input-control-prefix:var(--input-control-fix-width)}.has-suffix{--input-control-suffix:var(--input-control-fix-width)}.has-prefix-sm{--input-control-prefix:var(--input-control-fix-width-sm)}.has-suffix-sm{--input-control-suffix:var(--input-control-fix-width-sm)}.has-prefix-lg{--input-control-prefix:var(--input-control-fix-width-lg)}.has-suffix-lg{--input-control-suffix:var(--input-control-fix-width-lg)}.has-prefix-icon{--input-control-prefix:32px}.has-suffix-icon{--input-control-suffix:32px}.input-control.has-prefix-icon{--input-control-prefix:32px}.input-control.has-suffix-icon{--input-control-suffix:32px}.input-control.has-prefix-icon>.input-control-prefix,.input-control.has-suffix-icon>.input-control-suffix{justify-content:center;padding:0}.input-control.size-sm>.form-control,.input-control.size-sm>.input-control-prefix,.input-control.size-sm>.input-control-suffix{font-size:.75rem;line-height:1rem}.input-control.size-lg>.form-control,.input-control.size-lg>.input-control-prefix,.input-control.size-lg>.input-control-suffix{font-size:1rem;line-height:1.5rem}:root{--label-bg:rgba(var(--color-canvas-rgb),.1);--label-border-color:var(--color-gray-300);--label-color:var(--color-gray-500);--label-radius:var(--radius)}.label{align-items:center;background-color:var(--label-bg);border-radius:var(--label-radius);color:var(--label-color);display:inline-flex;font-size:.75rem;height:1.25rem;justify-content:center;line-height:1rem;min-width:1.25rem;padding-left:.25rem;padding-right:.25rem;white-space:pre-line;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:var(--label-border-color)}.label.size-lg{font-size:.8125rem;height:1.5rem;line-height:1.25rem;min-width:1.5rem;padding-left:.5rem;padding-right:.5rem}.label.size-sm{height:1rem;min-width:1rem}.label-dot{aspect-ratio:1/1;border-radius:var(--radius-full);height:.5rem;min-width:0;padding-left:0;padding-right:0}.caret,.caret-down,.caret-left,.caret-right,.caret-up{align-items:center;display:inline-flex;height:.75rem;justify-content:center;position:relative;width:.75rem}.caret-down:before,.caret-left:before,.caret-right:before,.caret-up:before,.caret:before{display:block;height:.35rem;transform-origin:center;width:.35rem;--tw-translate-y:-1px;--tw-rotate:45deg;border-color:currentColor;border-width:0 1px 1px 0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-content:"";content:var(--tw-content)}.caret-left:before{--tw-translate-x:0.125rem;--tw-translate-y:0px;--tw-rotate:135deg}.caret-left:before,.caret-right:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.caret-right:before{--tw-translate-x:-0.125rem;--tw-translate-y:0px;--tw-rotate:-45deg}.caret-up:before{--tw-translate-y:0.125rem;--tw-rotate:225deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:focus>.caret,:hover>.caret{opacity:inherit}.disabled .caret,.readonly .caret{opacity:var(--caret-opacity,.5)}.listitem{align-items:center;border-radius:var(--radius);display:flex;flex-direction:row;flex-wrap:nowrap;gap:.5rem;padding:.25rem}.listitem.selected{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity))}.listitem.active{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.item-actions,.item-avatar,.item-icon,.item-trailing,.item-trailing-icon{flex:none}.item-icon.icon-EMPTY{display:none}.item-subtitle{font-size:.75rem;line-height:1rem;opacity:.8}.item-content,.item-text{flex:1 1 auto;min-width:0}.item-leading,.item-trailing{align-items:center;display:flex;flex:none;gap:.5rem}.item-checkbox{margin-right:-.5rem}.item-content{align-items:center;display:flex;gap:.5rem}.multiline>.item-content{align-items:flex-start;flex-direction:column;gap:.25rem}.list{list-style-type:none;padding-left:0}.list-heading-inner .item-title{font-weight:700;--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.list.is-nested-sub{padding-left:var(--list-indent)}.is-nested-collapsed{display:none}.nested-toggle-icon{align-items:center;border-radius:var(--radius);display:flex;height:1rem;justify-content:center;margin-right:-.25rem;width:1rem}.magnifier{display:inline-block;height:1em;position:relative;width:1em}.magnifier:after,.magnifier:before{border-radius:var(--radius-full);content:"";display:block;position:absolute}.magnifier:before{border:1px solid;height:.75em;width:.75em}.magnifier:after{height:1px;left:.55em;top:.7em;width:.5em;--tw-rotate:45deg;background-color:currentColor;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.close{aspect-ratio:1/1;display:block;position:relative;width:1em}.close:after,.close:before{content:"";display:block;height:1px;left:0;position:absolute;width:100%;--tw-rotate:45deg;background-color:currentColor;border-radius:var(--radius-full);top:calc(50% - .5px)}.close:after,.close:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.close:after{--tw-rotate:-45deg}.search-box.compact.has-prefix-icon{--input-control-prefix:24px}.search-box.compact.has-suffix-icon{--input-control-suffix:24px}:root{--menu-bg:var(--color-canvas);--menu-active-bg:var(--color-primary-500);--menu-active-color:var(--color-canvas);--menu-selected-bg:var(--color-primary-50);--menu-selected-color:var(--color-primary-500);--menu-icon-opacity:.5;--menu-icon-margin:1.75rem;--menu-min-width:3rem;--menu-heading-color:var(--color-gray-500)}.menu{background:var(--menu-bg);max-width:var(--menu-max-width);min-width:var(--menu-min-width);padding:.5rem}.menu-wrapper{display:flex;flex-direction:column;min-height:0}.menu-wrapper>.menu{flex:1 1 auto;min-height:0;overflow:auto}.menu-item>.item-inner,.menu-item>a{align-items:center;border-radius:var(--radius);color:inherit;cursor:pointer;display:flex;gap:.5rem;justify-content:space-between;overflow:hidden;padding:.25rem .5rem;position:relative;text-overflow:ellipsis;transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);white-space:nowrap}.menu-item>.selected{background:var(--menu-selected-bg);color:var(--menu-selected-color)}.menu-item>.active,.menu-item>a.hover,.menu-item>a:hover{background:var(--menu-active-bg);color:var(--menu-active-color)}.menu-item.disabled>a:hover,.menu-item>a.disabled:hover{background-color:inherit;color:inherit}.menu-item>a>.item-title{flex:1 1 auto}.menu>.divider{margin:.25rem}.menu-heading{align-items:center;color:var(--menu-heading-color);display:flex;font-size:.75rem;font-weight:700;height:2rem;line-height:1rem;padding-left:.25rem;padding-right:.25rem}.has-icons>.menu-item>a>.item-icon{align-items:center;display:flex;height:1rem;justify-content:center;opacity:var(--menu-icon-opacity);width:1rem}.menu-nested{--menu-indent:10px}.menu-nested .menu{background-color:transparent;margin-bottom:-.5rem;margin-left:var(--menu-indent);margin-top:-.5rem;padding-right:0}.menu-nested .menu-item>.item-inner{padding-left:0}.menu-nested .search-menu-footer,.menu-nested .search-menu-header{margin-bottom:.25rem;margin-left:var(--menu-indent);margin-top:-.125rem;padding-right:1px}.menu.compact .menu-item>a{padding-bottom:.125rem;padding-top:.125rem}.menu.compact .menu-heading{height:1.75rem}.search-menu-header{padding:.5rem .5rem .125rem}.search-menu-footer{padding:.125rem .5rem .5rem}.search-menu .item.is-not-match,.search-menu-footer:empty,.search-menu-header:empty{display:none}.search-menu .item.is-not-match.has-match-child{display:list-item}.search-menu .item.is-not-match.has-match-child>.item-inner{opacity:.5;pointer-events:none}.search-menu.no-toggle-on-search .nested-toggle-icon{pointer-events:none}.search-menu .is-match-keys{font-weight:700;text-decoration-line:underline}.search-menu-no-match-hint{display:none;font-size:.75rem;line-height:1rem;padding:.5rem;--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.no-match-child .search-menu-no-match-hint{display:block}.no-match-child>.menu{display:none}:root{--alert-bg:var(--color-surface)}.alert{align-items:center;background-color:var(--alert-bg);border-radius:var(--radius);display:flex;gap:.75rem;padding:.5rem .75rem}.alert-content{display:flex;flex:1 1 auto;flex-direction:column;gap:1rem}.alert-close{margin-bottom:-.375rem;margin-right:-.5rem;margin-top:-.375rem}.alert-link{color:inherit;font-weight:700}.alert-link:hover{color:inherit;text-decoration-line:underline}.alert-heading{font-weight:700;margin:0}.alert-text{flex:1 1 auto;white-space:pre-line}.alert-actions{align-items:center;display:flex;gap:.5rem}.close{aspect-ratio:1/1;display:block;position:relative;width:1em}.close:after,.close:before{content:"";display:block;height:1px;left:0;position:absolute;width:100%;--tw-rotate:45deg;background-color:currentColor;border-radius:var(--radius-full);top:calc(50% - .5px)}.close:after,.close:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.close:after{--tw-rotate:-45deg}.messagers{align-items:flex-end;bottom:0;display:flex;flex-direction:column;gap:.5rem;justify-content:flex-end;left:0;padding:1rem;pointer-events:none;position:fixed;right:0;top:0;z-index:1900}.messagers-top-left,.messagers-top-right{justify-content:flex-start}.messagers-bottom-left,.messagers-top-left{align-items:flex-start}.messagers-bottom,.messagers-center,.messagers-top{align-items:center}.messagers-top,.messagers-top-left,.messagers-top-right{justify-content:flex-start}.messagers-center{justify-content:center}.messager{background-color:rgba(var(--color-inverse-rgb),.8);pointer-events:auto;--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity));--tw-shadow:0 10px 25px -5px rgba(0,0,0,.2),0 4px 10px -6px rgba(0,0,0,.15);--tw-shadow-colored:0 10px 25px -5px var(--tw-shadow-color),0 4px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);transition-duration:.3s}:root{--progress-radius:var(--radius);--progress-striped-size:40px;--progress-bg:var(--color-surface);--progress-bar-color:var(--color-primary-500)}.progress{background:var(--progress-bg);border-radius:var(--progress-radius);display:flex;height:1.25rem}.progress-bar{background:var(--progress-bar-color);height:100%;transition-duration:.5s;transition-property:width;transition-timing-function:cubic-bezier(.4,0,.2,1)}.progress-striped>.progress-bar{background-image:linear-gradient(45deg,rgba(var(--color-canvas-rgb),.15) 25%,transparent 25%,transparent 50%,rgba(var(--color-canvas-rgb),.15) 50%,rgba(var(--color-canvas-rgb),.15) 75%,transparent 75%,transparent);background-size:var(--progress-striped-size) var(--progress-striped-size)}.progress>.progress-bar:first-child{border-bottom-left-radius:inherit;border-top-left-radius:inherit}.progress>.progress-bar:last-child{border-bottom-right-radius:inherit;border-top-right-radius:inherit}.progress.active .progress-bar{animation:progress-bar-stripes 2s linear infinite}@keyframes progress-bar-stripes{0%{background-position:40px 0}to{background-position:0 0}}.scrollbar,:root{--scrollbar-size:10px;--scrollbar-opacity:.6;--scrollbar-bg:rgba(var(--color-inverse-rgb),.15);--scrollbar-inset:inset 0 0 0 1px rgba(var(--color-inverse-rgb),.05);--scrollbar-bar-bg:rgba(var(--color-inverse-rgb),.3);--scrollbar-hover-bg:rgba(var(--color-inverse-rgb),.4);--scrollbar-drag-bg:rgba(var(--color-inverse-rgb),.5);--scrollbar-radius:var(--radius-sm);--scrollbar-duration:.7s}::-webkit-scrollbar{height:var(--scrollbar-size);width:var(--scrollbar-size)}::-webkit-scrollbar-track{box-shadow:var(--scrollbar-shadow);-webkit-transition:background-color var(--scrollbar-duration);transition:background-color var(--scrollbar-duration)}::-webkit-scrollbar-track:hover,:hover::-webkit-scrollbar-track{background:var(--scrollbar-bg)}::-webkit-scrollbar-thumb{background:var(--scrollbar-bar-bg);border-radius:var(--scrollbar-radius);min-height:var(--scrollbar-size);-webkit-transition:var(--scrollbar-duration);transition:var(--scrollbar-duration);-webkit-transition-property:background-color;transition-property:background-color}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-hover-bg)}::-webkit-scrollbar-thumb:active{background:var(--scrollbar-drag-bg)}@-moz-document url-prefix(){.scrollbar-thin{scrollbar-width:thin}}.scrollbar-thin::-webkit-scrollbar{height:calc(var(--scrollbar-size)/2);width:calc(var(--scrollbar-size)/2)}.scrollbar-hover::-webkit-scrollbar,.scrollbar-hover::-webkit-scrollbar-thumb,.scrollbar-hover::-webkit-scrollbar-track{visibility:hidden}.scrollbar-hover:hover::-webkit-scrollbar,.scrollbar-hover:hover::-webkit-scrollbar-thumb,.scrollbar-hover:hover::-webkit-scrollbar-track{visibility:visible}@font-face{font-family:ZentaoIcon;font-style:normal;font-weight:400;src:url(./icons/ZentaoIcon.eot?v=1.69);src:url(./icons/ZentaoIcon.woff?v=1.69) format("woff"),url(./icons/ZentaoIcon.ttf?v=1.69) format("truetype"),url(./icons/ZentaoIcon.svg?v=1.69#regular) format("svg")}.icon,[class*=" icon-"],[class^=icon-]{display:inline-block;font-display:block;font-family:ZentaoIcon;font-size:14px;font-style:normal;font-variant:normal;font-weight:400;line-height:1;text-transform:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon:before{display:inline-block;min-width:14px;text-align:center}.icon-xs:before{font-size:.7em}.icon-sm:before{font-size:.8em}.icon-lg:before{font-size:1.25em;vertical-align:-10%}.icon-2x{font-size:28px}.icon-3x{font-size:42px}.icon-4x{font-size:56px}.icon-5x{font-size:70px}.icon-run{position:relative}.icon-run:before{opacity:.5}.icon-run:after{bottom:0;content:"\ea07";display:block;left:0;position:absolute;right:0;text-align:center;top:0}.icon-guihua:before{content:"\ea02"}.icon-memory:before{content:"\e9fd"}.icon-cpu:before{content:"\e9fe"}.icon-pause-pure:before{content:"\e9ff"}.icon-exclamation-pure:before{content:"\ea00"}.icon-zentao:before{content:"\e901"}.icon-zentao-alt:before{content:"\e900"}.icon-safe:before{content:"\ea06"}.icon-ai:before{content:"\e983"}.icon-kanban:before{content:"\e997"}.icon-college:before{content:"\e9c8"}.icon-home:before,.icon-menu-my:before{content:"\e97a"}.icon-program:before{content:"\e9aa"}.icon-lightbulb-alt:before,.icon-product:before{content:"\e98f"}.icon-project:before,.icon-rocket:before{content:"\e99c"}.icon-run:before{content:"\e9a9"}.icon-run-alt:before{content:"\ea07"}.icon-test:before{content:"\e956"}.icon-devops:before,.icon-infinite:before{content:"\e9a3"}.icon-doc:before,.icon-menu-doc:before{content:"\e99b"}.icon-statistic:before{content:"\e999"}.icon-group:before,.icon-menu-users:before,.icon-persons:before,.icon-team:before{content:"\e97e"}.icon-assets:before,.icon-diamond:before{content:"\e9ae"}.icon-flow:before{content:"\e994"}.icon-backend:before,.icon-cog-outline:before{content:"\ea05"}.icon-help:before{content:"\e968"}.icon-download:before,.icon-import:before{content:"\e904"}.icon-export:before{content:"\e905"}.icon-lightbulb:before{content:"\e91c"}.icon-close:before{content:"\e936"}.icon-check:before{content:"\e5ca"}.icon-minus:before{content:"\e926"}.icon-expand-alt:before{content:"\e6f1"}.icon-collapse-alt:before{content:"\e6f2"}.icon-fullscreen:before{content:"\e96b"}.icon-star-empty:before{content:"\e94a"}.icon-star:before{content:"\e94b"}.icon-exclamation-sign:before{content:"\e930"}.icon-info-sign:before{content:"\e9d5"}.icon-flag:before{content:"\e937"}.icon-check-circle:before{content:"\e92f"}.icon-check-sign:before{content:"\e938"}.icon-chart-pie:before{content:"\e95b"}.icon-history:before{content:"\e95f"}.icon-pencil:before{content:"\e254"}.icon-search:before{content:"\e928"}.icon-restart:before{content:"\e95e"}.icon-cog:before{content:"\e93b"}.icon-chart-line:before{content:"\e95c"}.icon-bar-chart:before,.icon-chart-bar:before{content:"\e95d"}.icon-exchange:before{content:"\e927"}.icon-severity:before{content:"\e973"}.icon-book:before{content:"\f02d"}.icon-treemap-alt:before{content:"\e971"}.icon-severity-solid:before{content:"\e902"}.icon-chat-line:before{content:"\e998"}.icon-stack:before{content:"\e943"}.icon-cube:before{content:"\e967"}.icon-minus-sign:before{content:"\e939"}.icon-bars-sign:before{content:"\e93a"}.icon-chat:before,.icon-message:before{content:"\e940"}.icon-more:before{content:"\e744"}.icon-certificate:before{content:"\f0a3"}.icon-bell:before{content:"\e7f5"}.icon-columns:before{content:"\f0db"}.icon-envelope-o:before{content:"\e92a"}.icon-unfold-all:before{content:"\e931"}.icon-fold-all:before{content:"\e932"}.icon-bars:before{content:"\e948"}.icon-cards-view:before{content:"\e949"}.icon-ellipsis-v:before{content:"\e5d4"}.icon-spinner-indicator:before{content:"\e982"}.icon-up-circle:before{content:"\e92b"}.icon-right-circle:before{content:"\e92c"}.icon-down-circle:before{content:"\e92d"}.icon-left-circle:before{content:"\e92e"}.icon-angle-double-right:before{content:"\f101"}.icon-angle-down:before{content:"\e313"}.icon-angle-left:before{content:"\e314"}.icon-angle-right:before{content:"\e315"}.icon-angle-top:before{content:"\e316"}.icon-first-page:before{content:"\e5dc"}.icon-last-page:before{content:"\e5dd"}.icon-caret-down:before{content:"\f0d7"}.icon-caret-up:before{content:"\f0d8"}.icon-caret-left:before{content:"\f0d9"}.icon-caret-right:before{content:"\f0da"}.icon-sort:before{content:"\f0dc"}.icon-sort-down:before{content:"\f0dd"}.icon-sort-up:before{content:"\f0de"}.icon-arrow-up:before{content:"\e923"}.icon-arrow-down:before{content:"\e924"}.icon-arrow-left:before{content:"\e952"}.icon-arrow-right:before{content:"\e93e"}.icon-chevron-left:before{content:"\e934"}.icon-chevron-right:before{content:"\e935"}.icon-chevron-double-up:before{content:"\e959"}.icon-chevron-double-down:before{content:"\e95a"}.icon-folder-account:before{content:"\e942"}.icon-folder-move:before{content:"\e960"}.icon-folder-plus:before{content:"\e961"}.icon-folder-upload:before{content:"\e962"}.icon-folder-star:before{content:"\e963"}.icon-folder-edit:before{content:"\e964"}.icon-folder-download:before{content:"\e965"}.icon-folder-outline:before{content:"\e966"}.icon-folder:before{content:"\e944"}.icon-folder-o:before{content:"\e945"}.icon-folder-open-o:before{content:"\e946"}.icon-folder-open:before{content:"\e947"}.icon-color:before{content:"\e93c"}.icon-paper-clip:before{content:"\e93d"}.icon-text:before{content:"\e929"}.icon-share:before{content:"\f064"}.icon-format-list-bulleted:before{content:"\e9a8"}.icon-format-bold:before{content:"\e953"}.icon-format-header-pound:before{content:"\e954"}.icon-format-italic:before{content:"\e955"}.icon-format-list-numbers:before{content:"\e969"}.icon-format-quote-close:before{content:"\e96a"}.icon-image:before{content:"\e96c"}.icon-table-large:before{content:"\e96d"}.icon-productplan:before{content:"\e9fc"}.icon-aiux:before{content:"\e99e"}.icon-qc:before{content:"\e986"}.icon-qc-q:before{content:"\e985"}.icon-qc-c:before{content:"\e987"}.icon-sonarqube:before{content:"\e9ba"}.icon-board:before{content:"\ea15"}.icon-market:before{content:"\ea01"}.icon-ztool:before{content:"\e9c1"}.icon-contacts:before{content:"\e9c3"}.icon-chats:before{content:"\e9c4"}.icon-ops:before{content:"\e903"}.icon-menu-backend:before{content:"\e993"}.icon-feedback:before{content:"\e991"}.icon-oa:before{content:"\e9a1"}.icon-more-circle:before{content:"\e988"}.icon-controls:before{content:"\e995"}.icon-account:before{content:"\e992"}.icon-about:before,.icon-info:before{content:"\e996"}.icon-exclamation:before{content:"\e9fa"}.icon-exit:before{content:"\e99a"}.icon-theme:before{content:"\e9a0"}.icon-globe:before,.icon-lang:before{content:"\f0ac"}.icon-template:before{content:"\e9fb"}.icon-seal:before{content:"\e9f8"}.icon-menu-arrow-left:before{content:"\e9f0"}.icon-menu-arrow-right:before{content:"\e9f1"}.icon-add-directory:before{content:"\e9ef"}.icon-design:before{content:"\e9ec"}.icon-download-alt:before{content:"\e9ea"}.icon-blame:before{content:"\e9e0"}.icon-draft-edit:before{content:"\e9dc"}.icon-sub-review-user:before{content:"\e9db"}.icon-sub-review:before{content:"\e9df"}.icon-ztf:before{content:"\e9dd"}.icon-save:before{content:"\e9d8"}.icon-list-box:before{content:"\e9b4"}.icon-usecase:before{content:"\e99d"}.icon-code:before{content:"\e990"}.icon-summary:before{content:"\e9ad"}.icon-more-alt:before{content:"\e9a7"}.icon-customer:before{content:"\e9d9"}.icon-alarm:before{content:"\ea16"}.icon-treeview:before{content:"\ea0d"}.icon-or:before{content:"\ea0b"}.icon-manager:before{content:"\ea0c"}.icon-target:before{content:"\ea0a"}.icon-ipd:before{content:"\e9f9"}.icon-wiki-lib:before{content:"\e9f5"}.icon-annex-lib:before{content:"\e9f6"}.icon-interface-lib:before{content:"\e9f7"}.icon-has-authority-pack:before{content:"\e9f3"}.icon-without-authority-pack:before{content:"\e9f4"}.icon-data-structure:before{content:"\e9f2"}.icon-waterfallplus:before{content:"\e9ed"}.icon-agileplus:before{content:"\e9ee"}.icon-horn:before{content:"\e9eb"}.icon-resume:before{content:"\e9e9"}.icon-mirror:before{content:"\e9e6"}.icon-remote:before{content:"\e9e7"}.icon-moon:before{content:"\e9e8"}.icon-ticket:before{content:"\e9e1"}.icon-gantt-alt:before{content:"\e9e2"}.icon-tree:before{content:"\e9c9"}.icon-list:before{content:"\e9cb"}.icon-gantt:before{content:"\e9cc"}.icon-group-view:before{content:"\e9cd"}.icon-inherit-space:before{content:"\e9c2"}.icon-card-archive:before{content:"\e9b8"}.icon-col-archive:before{content:"\e9b9"}.icon-col-add-right:before{content:"\e9bb"}.icon-col-add-left:before{content:"\e9bc"}.icon-col-split:before{content:"\e9bd"}.icon-waterfall:before{content:"\e9a4"}.icon-manual:before{content:"\e98d"}.icon-lane:before{content:"\e9b1"}.icon-mobile:before{content:"\ea18"}.icon-clear:before{content:"\ea19"}.icon-db:before{content:"\ea17"}.icon-unpinned:before{content:"\ea08"}.icon-pinned:before{content:"\ea09"}.icon-support-ticket:before{content:"\ea04"}.icon-snap-house:before{content:"\e9e3"}.icon-wrench:before{content:"\e9e4"}.icon-desktop:before{content:"\e9e5"}.icon-back-circle:before{content:"\e9da"}.icon-back:before{content:"\e9d3"}.icon-shield:before{content:"\e9ca"}.icon-meh:before{content:"\e9ce"}.icon-frown:before{content:"\e9cf"}.icon-smile:before{content:"\e9d0"}.icon-unlock-solid:before{content:"\e9d1"}.icon-lock-solid:before{content:"\e9d2"}.icon-ver:before{content:"\e9c6"}.icon-publish:before,.icon-send:before{content:"\e9c7"}.icon-tag:before{content:"\e9be"}.icon-tag-lock:before{content:"\e9bf"}.icon-code-fork:before{content:"\f126"}.icon-branch-lock:before{content:"\e9c0"}.icon-groups:before{content:"\e9af"}.icon-thumbs-up:before{content:"\f087"}.icon-thumbs-down:before{content:"\f088"}.icon-thumbs-up-solid:before{content:"\e9d6"}.icon-thumbs-down-solid:before{content:"\e9d7"}.icon-hash:before,.icon-version:before{content:"\e9ab"}.icon-p-square:before{content:"\e97b"}.icon-video-play:before{content:"\e97f"}.icon-plus-solid-circle:before{content:"\e974"}.icon-minuse-solid-circle:before{content:"\e9b6"}.icon-s:before{content:"\e975"}.icon-c:before{content:"\e976"}.icon-t:before{content:"\e977"}.icon-guide:before{content:"\e978"}.icon-todo:before{content:"\e979"}.icon-plus:before{content:"\e925"}.icon-side-left:before{content:"\e9b3"}.icon-side-right:before{content:"\e9b2"}.icon-fullscreen-exit:before{content:"\e972"}.icon-alert:before{content:"\e99f"}.icon-undo:before{content:"\e93f"}.icon-redo:before{content:"\e9d4"}.icon-swap:before{content:"\e9b0"}.icon-chat-solid:before{content:"\e9b5"}.icon-clock:before{content:"\e97c"}.icon-cost:before{content:"\e97d"}.icon-pencil-alt:before{content:"\e984"}.icon-size-height:before{content:"\e9c5"}.icon-upload-file:before{content:"\ea03"}.icon-file-log:before{content:"\e9de"}.icon-rich-text:before{content:"\e913"}.icon-markdown:before{content:"\e916"}.icon-excel:before{content:"\e933"}.icon-text-link:before{content:"\e94d"}.icon-ppt:before{content:"\e957"}.icon-word:before{content:"\e958"}.icon-doc-lib:before{content:"\e96f"}.icon-file-empty:before,.icon-file:before{content:"\f016"}.icon-file-text:before{content:"\f0f6"}.icon-file-alt:before{content:"\f15b"}.icon-file-text-alt:before{content:"\f15c"}.icon-file-pdf:before{content:"\f1c1"}.icon-file-word:before{content:"\f1c2"}.icon-file-excel:before{content:"\f1c3"}.icon-file-powerpoint:before{content:"\f1c4"}.icon-file-image:before{content:"\f1c5"}.icon-file-archive:before{content:"\f1c6"}.icon-file-audio:before{content:"\f1c7"}.icon-file-video:before{content:"\f1c8"}.icon-file-code:before{content:"\f1c9"}.icon-cell-input:before{content:"\ea0e"}.icon-input:before{content:"\ea0f"}.icon-checkbox:before{content:"\ea10"}.icon-radio:before{content:"\ea11"}.icon-transition:before{content:"\ea12"}.icon-add-sub-chapter:before{content:"\ea13"}.icon-add-chapter:before{content:"\ea14"}.icon-menu-collapse:before{content:"\e980"}.icon-menu-expand:before{content:"\e981"}.icon-estimate:before{content:"\e9ac"}.icon-sprint:before{content:"\e9a2"}.icon-shield-check:before{content:"\e9a5"}.icon-ok:before{content:"\e9a6"}.icon-printer:before{content:"\e906"}.icon-bullhorn:before{content:"\e910"}.icon-person:before{content:"\e941"}.icon-fields:before{content:"\e989"}.icon-trigger:before{content:"\e98a"}.icon-layout:before{content:"\e98b"}.icon-audit:before{content:"\e98c"}.icon-ban-circle:before,.icon-cancel:before{content:"\e951"}.icon-eye:before{content:"\e94e"}.icon-eye-off:before{content:"\e96e"}.icon-unlock:before{content:"\e94f"}.icon-lock:before,.icon-private:before{content:"\e950"}.icon-move:before{content:"\e94c"}.icon-hand-right:before{content:"\e907"}.icon-checked:before{content:"\e908"}.icon-off:before{content:"\e909"}.icon-play:before,.icon-start:before{content:"\e90a"}.icon-time:before{content:"\e90b"}.icon-edit:before{content:"\e90c"}.icon-trash:before{content:"\e90d"}.icon-link:before{content:"\e90e"}.icon-unlink:before{content:"\e90f"}.icon-bug:before{content:"\e911"}.icon-list-alt:before{content:"\e912"}.icon-alter:before,.icon-change:before{content:"\e970"}.icon-glasses:before,.icon-review:before{content:"\e914"}.icon-sitemap:before,.icon-testcase:before{content:"\e915"}.icon-pluses:before{content:"\e917"}.icon-report-list:before{content:"\e918"}.icon-active:before,.icon-magic:before{content:"\e919"}.icon-treemap:before{content:"\e91a"}.icon-confirm:before{content:"\e91b"}.icon-split:before{content:"\e98e"}.icon-calendar:before,.icon-delay:before{content:"\e91d"}.icon-pause:before{content:"\e91e"}.icon-ban:before{content:"\e91f"}.icon-plus-bold:before{content:"\e920"}.icon-copy:before{content:"\e921"}.icon-refresh:before{content:"\e922"}.icon-diff:before{content:"\e9b7"}.status-active,.status-asked,.status-normal,.status-unconfirmed,.status-wait{color:var(--color-status-normal,#313c52)}.status-active.status-issue,.status-checking,.status-commenting,.status-confirmed,.status-doing{color:#ff6f42}.status-blocked,.status-hangup,.status-pause,.status-suspended{color:#b89664}.status-clarify,.status-draft{color:#8166ee}.status-noreview,.status-reviewing,.status-wait.status-testcase{color:#18a6fd}.status-active.status-risk,.status-changed,.status-changing,.status-fail{color:#fb2b2b}.status-checked,.status-done,.status-pass,.status-replied,.status-resolved,.status-success{color:#0dbb7d}.status-cancel,.status-canceled,.status-investigate{color:var(--color-status-cancel,#838a9d)}.status-closed,.status-testtask.status-done{color:var(--color-status-closed,#9ea3b0)}.dark{--color-status-normal:#eff1f3;--color-status-cancel:#9ea3b0;--color-status-closed:#838a9d}@media (prefers-color-scheme:dark){.dark-auto{--color-status-normal:#eff1f3;--color-status-cancel:#9ea3b0;--color-status-closed:#838a9d}}.pri,[class*=" pri-"],[class^=pri-]{border-color:currentColor;border-radius:var(--radius-full);border-width:1px;display:inline-block;font-size:.75rem;height:1rem;line-height:1rem;line-height:14px;max-width:54px;min-width:16px;overflow:hidden;padding-left:.125rem;padding-right:.125rem;text-align:center;white-space:nowrap;--tw-text-opacity:1;color:rgba(var(--color-gray-400-rgb),var(--tw-text-opacity))}.pri-1{color:rgb(255 73 18/var(--tw-text-opacity))}.pri-1,.pri-2{--tw-text-opacity:1}.pri-2{color:rgba(var(--color-warning-600-rgb),var(--tw-text-opacity))}.pri-3{color:rgb(59 130 246/var(--tw-text-opacity))}.pri-3,.pri-4{--tw-text-opacity:1}.pri-4{color:rgb(149 211 224/var(--tw-text-opacity))}.severity{display:inline-block;font-size:.75rem;height:1rem;line-height:1rem;line-height:14px;max-width:54px;min-width:16px;overflow:hidden;padding-left:.125rem;padding-right:.125rem;position:relative;text-align:center;white-space:nowrap;--tw-text-opacity:1;color:rgba(var(--color-gray-400-rgb),var(--tw-text-opacity))}.severity:before{background-color:currentColor;background-repeat:no-repeat;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0;--tw-content:attr(data-severity);content:var(--tw-content);-webkit-mask:url('data:image/svg+xml;charset=utf-8,');mask:url('data:image/svg+xml;charset=utf-8,');-webkit-mask-size:100% 100%;mask-size:100% 100%}.severity:after,.severity:before{display:inline-block}.severity:after{--tw-scale-x:.75;--tw-scale-y:.75;content:attr(data-severity);line-height:20px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.severity[data-severity="1"]{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.severity[data-severity="2"]{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity))}.severity[data-severity="3"]{--tw-text-opacity:1;color:rgb(250 174 26/var(--tw-text-opacity))}.severity[data-severity="4"]{--tw-text-opacity:1;color:rgb(184 150 100/var(--tw-text-opacity))}.severity[data-severity="5"]{--tw-text-opacity:1;color:rgb(158 163 176/var(--tw-text-opacity))}.severity-label{background-color:transparent}.severity-label:after,.severity-label:before{--tw-content:none;content:var(--tw-content)}.risk-high{color:rgb(220 38 38/var(--tw-text-opacity))}.risk-high,.risk-middle{--tw-text-opacity:1}.risk-middle{color:rgb(234 179 8/var(--tw-text-opacity))}.label.label-id{border-radius:var(--radius-full);flex:none;--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-300-rgb),var(--tw-ring-opacity));--label-bg:rgba(var(--color-canvas-rgb),.1)}.nav-feature{--nav-active-bg:var(--color-primary-100);--nav-active-color:var(--color-fore)}.nav-feature .nav-checkbox>.checkbox,.nav-feature .nav-checkbox>.checkbox-primary,.nav-feature .nav-item>a{align-items:center;border-radius:var(--radius);height:2rem;padding-left:.625rem;padding-right:.625rem}.nav-feature .nav-item>a>.icon{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.sortable-list .listitem{cursor:move;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));padding-left:.5rem;padding-right:.5rem}.sortable-list .sortable-ghost>.listitem{--tw-bg-opacity:1;background-color:rgba(var(--color-warning-50-rgb),var(--tw-bg-opacity))}.sortable-empty-shadow{height:1px!important;left:0!important;opacity:0!important;pointer-events:none!important;position:absolute!important;top:0!important;width:1px!important}:root{--avatar-radius:12.5%;--avatar-bg:var(--color-surface)}.avatar{align-items:center;aspect-ratio:1/1;background:var(--avatar-bg);border-radius:var(--avatar-radius);display:inline-flex;justify-content:center;overflow:hidden;width:2rem}.avatar>img{height:100%;margin:0;-o-object-fit:cover;object-fit:cover;width:100%}.avatar.size-xs{width:1.25rem}.avatar.size-sm,.avatar.size-xs{font-size:.75rem;line-height:1rem}.avatar.size-sm{width:1.5rem}.avatar.size-lg{font-size:1.5rem;line-height:2rem;width:3rem}.avatar.size-xl{font-size:2.25rem;line-height:2.5rem;width:5rem}.avatar-group{display:flex;gap:.625rem}.avatar-group>.avatar{box-shadow:0 0 0 1px var(--color-canvas)}.avatar-group>*+*{margin-left:-1rem}.avatar-group.size-xs>*+*{margin-left:-.75rem}.avatar-group.size-sm>*+*{margin-left:-.875rem}.avatar-group.size-lg>*+*{margin-left:-1.25rem}.avatar-group.size-xl>*+*{margin-left:-1.5rem}.btn-group{display:flex;gap:1px}.btn-group>.btn.active{z-index:1}.btn-group>.btn:focus-within,.btn-group>.btn:hover{z-index:2}.btn-group>.btn.disabled,.btn-group>.btn:disabled{z-index:0}.btn-group>.btn:not(:first-child){border-bottom-left-radius:var(--radius-none);border-left-color:rgba(var(--color-canvas-rgb),.2);border-left-width:1px;border-top-left-radius:var(--radius-none)}.btn-group>.btn:not(:first-child):before{left:-1px}.btn-group>.btn:not(:last-child){border-bottom-right-radius:var(--radius-none);border-top-right-radius:var(--radius-none)}.color-picker-heading{align-items:center;font-weight:700;justify-content:space-between;margin-bottom:-.25rem;padding:.5rem}.color-picker-heading,.color-picker-row{display:flex;flex-direction:row}.color-picker-row{flex-wrap:wrap;gap:.5rem;padding:.75rem}.color-picker-item{align-items:center;border-radius:var(--radius-full);display:flex;height:1.25rem;justify-content:center;padding-left:.125rem;padding-right:.125rem;width:1.25rem;--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity));--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.pick-container{left:0;pointer-events:none;position:absolute;top:0;z-index:1600}.pick-pop{display:flex;flex-direction:column;opacity:0;pointer-events:auto;position:absolute;transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);z-index:1200}.pick-pop.in{opacity:1}.date-picker-menu{--menu-bg:none}.date-picker-menu .mini-calendar{margin-top:-.75rem}.date-pick-menu-month,.date-pick-menu-years{display:flex;flex-direction:row;flex-wrap:wrap;gap:1px;max-height:237px;padding-bottom:.5rem;padding-left:.5rem;padding-right:.5rem}.date-pick-menu-years>.btn{width:2.5rem}.date-pick-menu-years>.btn.is-current{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-100-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.date-pick-menu-month>.btn.active,.date-pick-menu-years>.btn.active{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.date-pick-menu-month>.btn{width:3rem}.date-picker-menu-footer{border-top-width:1px;display:flex;flex-direction:row;padding:.25rem}.date-picker-menu-footer>.toolbar{flex:1 1 auto;justify-content:center}.date-picker-menu>.menu{border-right-width:1px}.date-picker-menu .menu-item{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.time-picker-menu{max-height:inherit}.time-picker-menu .menu-item>a{justify-content:center}.datetime-picker-menu{max-height:inherit}.mini-calendar{padding:.5rem}.mini-calendar>.row>.col{align-items:center;justify-content:center;width:14.2857%}.mini-calendar-day{height:2rem}.mini-calendar-day.disabled{pointer-events:none;--tw-bg-opacity:1;background-color:rgba(var(--color-gray-200-rgb),var(--tw-bg-opacity));opacity:.5}.mini-calendar-day>.btn{width:2rem}.mini-calendar-day.is-weekend>.btn{--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.mini-calendar-day.active>.btn,.mini-calendar-day.is-today>.btn{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-100-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.mini-calendar-day.selected>.btn{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.mini-calendar-day.is-out-month{opacity:.5}.i-time{border-color:currentColor;border-radius:50%;border-width:.08em;display:inline-block;height:1em;position:relative;width:1em}.i-time:after{border-color:currentColor;border-width:0 0 .08em .08em;height:.385em;left:.3846em;position:absolute;top:.08em;width:.385em;--tw-content:"";content:var(--tw-content)}.chevron-down,.chevron-left,.chevron-right,.chevron-up{align-items:center;display:inline-flex;height:1em;justify-content:center;position:relative;width:1em}.chevron-down:before,.chevron-left:before,.chevron-right:before,.chevron-up:before{display:block;height:.6923077em;transform-origin:center;width:.6923077em;--tw-translate-y:-0.125rem;--tw-rotate:45deg;border-color:currentColor;border-width:0 1px 1px 0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-content:"";content:var(--tw-content)}.chevron-left:before{--tw-translate-y:0px;--tw-translate-x:0.125rem;--tw-rotate:135deg}.chevron-left:before,.chevron-right:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.chevron-right:before{--tw-translate-y:0px;--tw-translate-x:-0.125rem;--tw-rotate:-45deg}.chevron-up:before{--tw-translate-y:0.125rem;--tw-rotate:225deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.i-calendar{border-radius:2px;display:inline-block;height:.79em;margin-top:.16em;position:relative;width:1em}.i-calendar,.i-calendar:after,.i-calendar:before{border-color:currentColor;border-width:.08em}.i-calendar:after,.i-calendar:before{position:absolute;--tw-content:"";content:var(--tw-content)}.i-calendar:before{border-bottom-width:0;left:.15em;top:.307em;width:.6em}.i-calendar:after{border-bottom-width:0;border-top-width:0;height:.3077em;left:.17em;top:-.154em;width:.538em}:root{--modal-radius:var(--radius-lg);--modal-bg:rgba(var(--color-inverse-rgb),.4);--modal-sm:37.5rem;--modal-base:50rem;--modal-lg:75rem;--modal-xl:90rem}.modal{align-items:center;background-color:var(--modal-bg);bottom:0;display:none;justify-content:center;left:0;overflow:hidden;position:fixed;right:0;top:0;z-index:1500}.modal.load-indicator:before{background-color:transparent;transition-property:none}.modal.loading:after{--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.modal.loading .modal-dialog{opacity:0!important}.modal.show{display:flex}.modal-no-backdrop{background-color:transparent;pointer-events:none}.modal-hide{opacity:0!important}.modal-dialog{background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));pointer-events:auto;position:relative;--tw-bg-opacity:0.9;--tw-shadow:0 10px 50px -10px rgba(0,0,0,.4),0 8px 20px -10px rgba(0,0,0,.3);--tw-shadow-colored:0 10px 50px -10px var(--tw-shadow-color),0 8px 20px -10px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.05;--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);border-radius:var(--modal-radius);width:var(--modal-base)}.modal-content,.modal-dialog{max-height:100vh}.modal-content{display:flex;flex-direction:column;height:100%}.loading>.modal-dialog>.modal-content{opacity:0;visibility:hidden}.close{aspect-ratio:1/1;display:block;position:relative;width:1em}.close:after,.close:before{content:"";display:block;height:1px;left:0;position:absolute;width:100%;--tw-rotate:45deg;background-color:currentColor;border-radius:var(--radius-full);top:calc(50% - .5px)}.close:after,.close:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.close:after{--tw-rotate:-45deg}.modal-header{align-items:center;display:flex;flex:none;flex-wrap:nowrap;gap:1rem;padding:1rem 1.25rem;position:relative}.modal-title{flex:1 1 auto;font-size:1rem;font-weight:700;line-height:1.5rem}.modal-actions{position:absolute;right:.75rem;top:.75rem;z-index:15}.modal-body{flex:1 1 auto;overflow:auto;padding:1rem 1.25rem}.modal-footer{align-items:center;display:flex;flex:none;gap:.75rem;padding:1.25rem}.modal-dialog.size-full,.modal-dialog[data-size=full]{border-radius:var(--radius-none);height:100vh;width:100vw}.modal-dialog.size-sm,.modal-dialog[data-size=sm]{width:var(--modal-sm)}.modal-dialog.size-lg,.modal-dialog[data-size=lg]{width:var(--modal-lg)}.modal-dialog.size-xl,.modal-dialog[data-size=xl]{width:var(--modal-xl)}.modal-iframe{flex:1 1 auto;width:100%}.modal-trans{opacity:0;transition-duration:.2s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.modal-trans.in{opacity:1}.modal-trans>.modal-dialog{transition-property:transform,opacity;--tw-scale-x:.95;--tw-scale-y:.95;opacity:0;transition-duration:.3s}.modal-trans.in>.modal-dialog,.modal-trans>.modal-dialog{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.modal-trans.in>.modal-dialog{--tw-scale-x:1;--tw-scale-y:1;opacity:1}.modal-alert .modal-footer{justify-content:flex-end}.modal-alert .modal-header{margin-bottom:-1rem}.modal-alert .modal-body{overflow-wrap:break-word;white-space:pre-line}.modal-alert .modal-actions+.modal-body{padding-right:3rem}.modal-alert .modal-header+.modal-actions+.modal-body{padding-right:1.25rem}.file-selector{gap:.5rem;width:100%}.file-selector,.file-selector-box{display:flex;flex-direction:column}.file-selector-box{border-style:dashed;border-width:1px;gap:0;height:auto;justify-content:center;position:relative;--tw-border-opacity:1;background-color:rgba(var(--color-primary-50-rgb),.3);border-color:rgba(var(--color-primary-400-rgb),var(--tw-border-opacity));padding:.5rem .75rem;--tw-ring-color:transparent;--state-color:rgba(var(--color-primary-500-rgb),.05)}.file-selector-box .text{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.is-dragging .file-selector-box{border-style:solid}.file-selector-btn{align-items:center;display:flex;gap:.5rem;position:relative}.is-dragging .file-selector-btn{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity))}.file-selector-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;bottom:0;cursor:pointer;left:0;opacity:.1;position:absolute;right:0;top:0}.file-selector-tip{--tw-text-opacity:1;color:rgba(var(--color-gray-400-rgb),var(--tw-text-opacity))}.file-selector.is-mode-button{flex-direction:column-reverse}.file-selector-list.is-empty{display:none}.file-selector-item .item-avatar{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-50-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.file-selector-item .item-subtitle{--tw-text-opacity:1;color:rgba(var(--color-gray-400-rgb),var(--tw-text-opacity))}.file-selector-item .item-content{flex:none;margin-right:.25rem;max-width:100%}.file-selector-item>.listitem:hover{background-color:rgba(var(--color-gray-500-rgb),.05)}.file-selector-item.is-renaming .item-avatar+.item-content{max-width:calc(100% - 32px)}.file-selector-rename-text{max-width:100%;min-width:100px;position:relative}.file-selector-rename-text>div{overflow:hidden;pointer-events:none;visibility:hidden;white-space:nowrap}.file-selector-rename-input{bottom:0;left:0;position:absolute;right:0;top:0}.file-selector-grid{align-items:flex-start;border-color:transparent;border-radius:var(--radius);border-width:1px;display:flex;flex-direction:row;flex-wrap:wrap;gap:var(--file-selector-grid-gap);--tw-bg-opacity:1;background-color:rgba(var(--color-surface-light-rgb),var(--tw-bg-opacity));padding:.5rem}.file-selector-grid-item{height:var(--file-selector-grid-height)}.file-selector-grid-btn,.file-selector-grid-item{width:var(--file-selector-grid-width)}.file-selector-grid-btn{aspect-ratio:1/1;border-style:dashed;border-width:1px;display:flex;flex-direction:column;gap:0;height:auto;justify-content:center;--tw-border-opacity:1;background-color:rgba(var(--color-primary-50-rgb),.3);border-color:rgba(var(--color-primary-400-rgb),var(--tw-border-opacity));padding:.5rem;--tw-ring-color:transparent;--state-color:rgba(var(--color-primary-500-rgb),.05)}.file-selector-grid-btn .text{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.file-selector-grid-item .listitem{flex-direction:column;height:100%;padding:0;position:relative;width:100%}.file-selector-grid-item .item-avatar{border-radius:var(--radius);border-width:1px;width:100%;--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.file-selector-grid-item .item-avatar.has-icon>.icon{font-size:calc(var(--file-selector-grid-width)/2)}.file-selector-grid-item .item-content,.file-selector-grid-item .toolbar{aspect-ratio:1/1;left:0;position:absolute;right:0;top:0;width:100%}.file-selector-grid-item .item-title{bottom:-1.25rem;font-size:.75rem;left:0;line-height:1rem;overflow:hidden;position:absolute;right:1.5rem;white-space:nowrap}.file-selector-grid-item .item-subtitle{border-radius:var(--radius-full);bottom:.25rem;left:.25rem;position:absolute;--tw-bg-opacity:1;background-color:rgba(var(--color-fore-rgb),var(--tw-bg-opacity));font-size:.75rem;line-height:1rem;padding-left:.25rem;padding-right:.25rem;--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity));opacity:0;transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.file-selector-grid-item:hover .item-subtitle{opacity:1}.file-selector-grid-item .toolbar .btn[data-remove-file]{background-color:rgba(var(--color-inverse-rgb),.5);padding:.375rem;position:absolute;right:1px;top:1px;--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.file-selector-grid-item .toolbar .btn[data-rename-file]{bottom:-1.5rem;padding:0;position:absolute;right:0}.file-selector-grid .file-selector-rename{bottom:0;height:1.5rem;position:absolute;top:auto}.file-selector-grid .file-selector-rename-text{width:100%}.is-dragging .file-selector-grid{border-width:1px;--tw-border-opacity:1;border-color:rgba(var(--color-primary-400-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity))}:root{--nav-radius:var(--radius);--nav-active-color:var(--color-primary-500);--nav-active-bg:var(--color-inherit);--nav-heading-color:var(--color-gray-500);--nav-gap:0.25rem;--nav-item-color:inherit;--nav-item-bg:transparent;--nav-item-padding:1rem;--nav-item-height:2rem;--nav-item-gap:0.75rem;--nav-divider-height:calc(var(--nav-item-height)/2);--nav-divider-margin:0.5rem}.nav{list-style-type:none;padding-left:0}.nav,.nav-item{align-items:center;display:flex;position:relative}.nav-item{gap:var(--nav-item-gap)}.nav-item>a{align-items:center;background-color:var(--nav-item-bg);color:var(--nav-item-color);cursor:pointer;display:flex;gap:var(--nav-gap);height:var(--nav-item-height);justify-content:center;padding-left:var(--nav-item-padding);padding-right:var(--nav-item-padding)}.nav-item>a>.text{white-space:nowrap}.nav-item>.active{font-weight:700;--nav-item-color:var(--nav-active-color);--nav-item-bg:var(--nav-active-bg)}.nav-item>.selected{--nav-item-color:var(--nav-selected-color);--nav-item-bg:var(--nav-selected-bg)}.nav-item>.disabled{--nav-item-color:var(--nav-disabled-color);--nav-item-bg:var(--nav-disabled-bg)}.nav-item>.disabled>*{opacity:var(--opacity-disabled)}.nav-divider,.nav>.divider{align-self:center;height:var(--nav-divider-height);margin-left:var(--nav-divider-margin);margin-right:var(--nav-divider-margin)}.nav-heading{align-items:center;color:var(--nav-heading-color);display:flex;font-weight:700;gap:.25rem;height:2rem;justify-content:center;padding-left:1rem;padding-right:1rem}.nav-space{flex:1 1 auto;width:1rem}.nav-compact{--nav-gap:0.25rem;--nav-item-gap:0.5rem;--nav-item-height:1.75rem;--nav-item-padding:0.5rem;--nav-divider-margin:0.25rem}.nav-primary>.nav-item{position:relative}.nav-primary>.nav-item:hover{z-index:10}.nav-primary>.nav-item+.nav-item{margin-left:-1px}.nav-primary>.nav-item>a{border-radius:var(--radius-none);border-width:1px}.nav-primary>.nav-item:first-child>a,.nav-primary>.nav-item:has(.nav-heading)+.nav-item>a{border-bottom-left-radius:var(--nav-radius);border-top-left-radius:var(--nav-radius)}.nav-primary>.nav-item:last-child>a{border-bottom-right-radius:var(--nav-radius);border-top-right-radius:var(--nav-radius)}.nav-primary>.nav-item>.active{--tw-text-opacity:1;background:var(--nav-active-color);border-color:var(--nav-active-color);color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.nav-primary>.nav-divider{display:none}.nav-secondary>.nav-item>a{border-radius:var(--radius-none);position:relative}.nav-secondary>.nav-item>a:after{bottom:-2px;display:block;height:2px;left:0;position:absolute;right:0;--tw-bg-opacity:1;background-color:rgba(var(--color-border-rgb),var(--tw-bg-opacity));--tw-content:"";content:var(--tw-content)}.nav-secondary>.nav-item>.active:after{background-color:currentColor}.nav-secondary>.nav-divider{margin:0 0 0 -1px}.nav-tabs>.nav-item>a{border-bottom-left-radius:var(--radius-none);border-bottom-right-radius:var(--radius-none);position:relative}.nav-tabs>.nav-item>a:after{border-color:transparent;border-width:1px;bottom:-1px;display:block;left:-1px;pointer-events:none;position:absolute;right:-1px;top:-1px;--tw-border-opacity:1;border-bottom-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));--tw-content:"";border-top-left-radius:inherit;border-top-right-radius:inherit;content:var(--tw-content)}.nav-tabs>.nav-item>.active:after{--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));border-bottom-color:transparent}.nav-tabs>.nav-divider{margin:0 0 0 -1px}.nav-pills>.nav-item>a{border-radius:var(--radius-full)}.nav-pills>.nav-item>.active{--tw-text-opacity:1;background:var(--nav-active-color);border-color:var(--nav-active-color);color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.nav-steps{--nav-item-bg:var(--color-canvas);--nav-active-bg:var(--color-primary-500);--nav-active-color:var(--color-canvas);--nav-selected-bg:var(--color-secondary-500);--nav-selected-color:var(--color-canvas)}.nav-steps>.nav-item+.nav-item>a{padding-left:calc(var(--nav-item-padding) + var(--nav-item-height)/3)}.nav-steps>.nav-item>a:after,.nav-steps>.nav-item>a:before{border-style:solid;height:0;left:100%;position:absolute;width:0;z-index:2;--tw-content:"";border-color:transparent;border-left-color:var(--nav-item-bg,var(--color-canvas));border-width:calc(var(--nav-item-height)/2);border-left-width:calc(var(--nav-item-height)/3);border-right-width:0;content:var(--tw-content)}.nav-steps>.nav-item>a:before{background-color:transparent!important;border-left-color:var(--color-border);bottom:auto;left:calc(100% + 5.5px);opacity:1!important;right:auto;top:.4rem;transform:none;z-index:1;--nav-item-height:1.2rem}.nav-steps>.nav-item:has(+.nav-item>.active)>.selected:before,.nav-steps>.nav-item:has(+.nav-item>.active,+.nav-item>.selected)>a:not(.active,.selected):before,.nav-steps>.nav-item:has(+.nav-item>.selected)>.active:before,.nav-steps>.nav-item:last-child>a:after,.nav-steps>.nav-item:last-child>a:before{display:none}.nav-steps>.nav-item>a:hover{--nav-item-bg:var(--color-gray-100)}.nav-steps>.nav-item>.active:hover{--nav-item-bg:var(--color-primary-600)}.nav-steps>.nav-item>.selected:hover{--nav-item-bg:var(--color-secondary-600)}.nav-stacked{flex-direction:column}.nav-stacked>.nav-item{width:100%}.nav-stacked>.nav-heading,.nav-stacked>.nav-item>a{justify-content:flex-start;width:100%}.nav-stacked>.nav-divider{height:1px;margin:.5rem 0;width:100%}.nav-primary.nav-stacked>.nav-item>a{height:2.5rem}.nav-primary.nav-stacked>.nav-item+.nav-item{margin-left:0;margin-top:-1px}.nav-primary.nav-stacked>.nav-item:first-child>a{border-bottom-left-radius:0;border-top-left-radius:var(--nav-radius);border-top-right-radius:var(--nav-radius)}.nav-primary.nav-stacked>.nav-item:last-child>a{border-bottom-left-radius:var(--nav-radius);border-bottom-right-radius:var(--nav-radius);border-top-right-radius:0}.nav-secondary.nav-stacked>.nav-item>a:after{bottom:0;height:100%;left:auto;right:0;top:0;width:2px;--tw-bg-opacity:1;background-color:rgba(var(--color-border-rgb),var(--tw-bg-opacity))}.nav-secondary.nav-stacked>.nav-item>.active:after{background-color:currentColor}.nav-secondary.nav-stacked>.nav-divider{margin:0}.nav-tabs.nav-stacked>.nav-item>a{border-bottom-left-radius:var(--nav-radius);border-bottom-right-radius:var(--radius-none);border-top-right-radius:var(--radius-none);position:relative}.nav-tabs.nav-stacked>.nav-item>a:after{--tw-border-opacity:1;border-bottom-color:transparent;border-bottom-left-radius:inherit;border-right-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));border-top-left-radius:inherit}.nav-tabs.nav-stacked>.nav-item>a.active:after{--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));border-right-color:transparent}.nav-tabs.nav-stacked>.nav-divider{margin:0}.nav-pills.nav-stacked>.nav-item+.nav-item{margin-top:.25rem}.nav-justified>.nav-item:not(.flex-none,.nav-divider){flex:1 1 auto}.nav-justified>.nav-item>a{width:100%}.arrow,.arrow:before{background:inherit;height:calc(var(--arrow-size)*1.4142);position:absolute;width:calc(var(--arrow-size)*1.4142)}.arrow{visibility:hidden;--arrow-size:5px}.arrow:before{border:inherit;content:"";visibility:visible;--tw-rotate:45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.arrow-top,.arrow-up{border-bottom:inherit;border-right:inherit;border-width:1px 0 0 1px;top:calc(1px - var(--arrow-size))}.white>.arrow-top,.white>.arrow-up{top:calc(0px - var(--arrow-size))}.arrow-bottom,.arrow-down{border-left:inherit;border-top:inherit;border-width:0 1px 1px 0;bottom:calc(1px - var(--arrow-size))}.white>.arrow-bottom,.white>.arrow-down{bottom:calc(0px - var(--arrow-size))}.arrow-left{border-right:inherit;border-top:inherit;border-width:0 0 1px 1px;left:calc(1px - var(--arrow-size))}.white>.arrow-left{left:calc(0px - var(--arrow-size))}.arrow-right{border-bottom:inherit;border-left:inherit;border-width:1px 1px 0 0;right:calc(1px - var(--arrow-size))}.white>.arrow-right{right:calc(0px - var(--arrow-size))}.popover{display:none;position:absolute;width:-moz-max-content;width:max-content}.popover.show{display:block}.popover-heading{align-items:center;border-top-left-radius:inherit;border-top-right-radius:inherit;display:flex;gap:1rem;height:2.5rem;justify-content:space-between;position:relative;z-index:1;--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity));padding-left:.75rem;padding-right:.5rem}.popover-title{flex:1 1 auto;font-weight:700}.popover-content{padding:.75rem;position:relative;z-index:1}.popover-arrow{z-index:0}.has-heading>.popover-arrow.arrow-top,.has-heading>.popover-arrow.arrow-up{--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity))}.popover>.btn-close{position:absolute;right:.5rem;top:.625rem;z-index:2}.btn-close+.popover-content{padding-right:3rem}.btn.with-popover-show{--tw-shadow:inset 0 2px 4px 0 rgba(0,0,0,.05);--tw-shadow-colored:inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.with-popover-show[data-pop-placement=top]>.caret{--tw-rotate:180deg}.with-popover-show[data-pop-placement=left]>.caret,.with-popover-show[data-pop-placement=top]>.caret{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.with-popover-show[data-pop-placement=left]>.caret{--tw-rotate:90deg}.with-popover-show[data-pop-placement=right]>.caret{--tw-rotate:-90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.pager{align-items:center;display:flex;gap:.25rem;white-space:nowrap}.menu.pager-size-menu{display:grid!important;grid-template-columns:repeat(3,minmax(0,1fr));text-align:center}.menu.pager-size-menu>.menu-item>a{margin:.125rem}.pager .pager-goto-group>.form-control{width:3rem}.pager .pager-goto-group>.input-group-addon{border-width:1px;--tw-border-opacity:1;border-color:rgba(var(--color-gray-300-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.pager>.pager-nav.active{--tw-text-opacity:1;box-shadow:0 0 0 1px var(--color-primary-500);color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.panel{border-radius:var(--radius);overflow:hidden;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.05}.panel-heading{background-color:var(--panel-heading-bg);flex-wrap:nowrap;gap:1rem;justify-content:space-between;padding:.5rem 1rem}.panel-heading,.panel-title{align-items:center;display:flex}.panel-title{font-weight:700;gap:.5rem;margin:0}.panel-actions{align-items:center;display:flex;margin-right:-.5rem}.panel-body{padding:.75rem 1rem}.panel-footer{align-items:center;background-color:var(--panel-footer-bg);display:flex;gap:.5rem;padding:.5rem 1rem}.size-sm .panel-body,.size-sm .panel-footer,.size-sm .panel-heading{padding:.375rem .75rem}.size-lg .panel-body,.size-lg .panel-footer,.size-lg .panel-heading{padding:1rem 1.25rem}.picker{position:relative}.picker-select{align-items:center;gap:.25rem;justify-content:space-between}.picker-select,.picker-select.form-control{display:flex}.picker-select-placeholder{flex:1 1 auto;--tw-text-opacity:1;color:rgba(var(--color-placeholder-rgb),var(--tw-text-opacity))}.picker-select-multi .picker-select-placeholder{padding-left:.25rem;padding-right:.25rem}.picker-single-selection{flex:1 1 auto;min-width:0;overflow:hidden;white-space:nowrap}.picker-deselect-btn{border-radius:var(--radius-full);padding:.25rem}.picker-deselect-btn:not(:hover){opacity:.7}.picker-select-multi.form-control{height:auto;min-height:32px;padding-left:.25rem}.picker-multi-selections{display:flex;flex-wrap:wrap;gap:.25rem;min-width:0}.picker-multi-selection{align-items:center;border-radius:var(--radius-full);border-width:1px;display:flex;max-width:100%;--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity));padding-left:.5rem}.readonly .picker-multi-selection{padding-right:.5rem}.picker-multi-selection>.text{max-width:180px;min-width:0;overflow:clip;white-space:nowrap}.picker-no-search{cursor:default}.picker-menu-list{border-style:none;flex:1 1 auto;min-height:0;overflow-y:auto;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.picker-menu-search+.picker-menu-list.menu{margin-top:-.125rem;padding-top:0}.picker-menu-list.tree{padding:.5rem}.picker-menu-list .tree-item>.selected .item-content{font-weight:400}.picker-menu .search-menu-footer{border-top-width:1px;padding-bottom:.25rem;padding-top:.25rem}.picker-search-input{border-color:transparent;height:1.75rem;outline-width:0!important;padding-left:0;padding-right:2rem;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important;--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)!important;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)!important}.picker-search{flex:none;position:relative;width:100%}.picker-search>.magnifier{opacity:.5;position:absolute;right:0;top:.5rem}.picker-search-clear.btn{cursor:pointer;height:1.25rem;position:absolute;right:0;top:.25rem;width:1.25rem}.picker-search.is-inline{min-width:64px;width:4rem}.is-inline>.picker-search-input{height:1.25rem;min-width:64px;padding-left:.25rem;padding-right:.25rem}.picker-search-measure{height:0;left:0;overflow:hidden;position:absolute;top:0;white-space:nowrap}.split{display:flex;flex-direction:row}.split-vert{flex-direction:column}.split-cell{min-height:0;min-width:0}.has-animation>.split-cell{transition-duration:.15s;transition-property:width,height;transition-timing-function:cubic-bezier(.4,0,.2,1)}.gutter{align-items:stretch;border-radius:var(--radius);cursor:col-resize;display:flex;flex:none;flex-direction:row;justify-content:center;position:relative}.gutter-vert{cursor:row-resize;flex-direction:column}.gutter:before{display:block;--tw-bg-opacity:1;background-color:rgba(var(--color-border-rgb),var(--tw-bg-opacity));transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);--tw-content:"";content:var(--tw-content)}.gutter-horz:before{width:1px}.gutter-vert:before{height:1px}.gutter-horz:hover:before{width:75%}.gutter-vert:hover:before{height:75%}.gutter-resize-handler,.gutter-toggle{align-items:center;border-radius:var(--radius-lg);border-width:1px;display:flex;height:1.75rem;justify-content:center;margin-top:-.875rem;position:absolute;top:50%;width:.75rem;--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-fore-rgb),var(--tw-text-opacity));transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.gutter-toggle>span{--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.gutter:hover .gutter-toggle{--tw-border-opacity:1;border-color:rgba(var(--color-gray-500-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-gray-100-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.gutter .gutter-toggle:hover{--tw-border-opacity:1;border-color:rgba(var(--color-primary-500-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.is-last>.gutter-toggle{--tw-scale-x:-1}.is-first>.gutter-toggle,.is-last>.gutter-toggle{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.is-first>.gutter-toggle{--tw-scale-x:1}.gutter-vert .gutter-toggle{left:50%;margin-left:-.375rem;margin-top:0;top:auto;--tw-rotate:90deg}.gutter-vert .gutter-toggle,.gutter.is-next-collapsed>.gutter-toggle{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.gutter.is-next-collapsed>.gutter-toggle{--tw-scale-x:1}.gutter.is-prev-collapsed>.gutter-toggle{--tw-scale-x:-1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.gutter-horz.is-first.is-prev-collapsed{transform:translateX(calc(-1px - var(--split-gutter-size)/2))}.gutter-horz.is-last.is-next-collapsed{transform:translateX(calc(1px + var(--split-gutter-size)/2))}.gutter-vert.is-first.is-prev-collapsed{transform:translateY(calc(-1px - var(--split-gutter-size)/2))}.gutter-vert.is-last.is-next-collapsed{transform:translateY(calc(1px + var(--split-gutter-size)/2))}.gutter-resize-handler{background-color:transparent;border-width:0}.gutter-resize-handler:before{border-left-width:1px;border-right-width:1px;bottom:0;left:.25rem;position:absolute;right:.25rem;top:0;--tw-content:"";content:var(--tw-content)}.sidebar{flex:none!important;position:relative;--gutter-width:1px;--sidebar-duration:200ms}.has-sidebar-animation .sidebar{transition-duration:.15s;transition-duration:var(--sidebar-duration);transition-property:width,margin;transition-timing-function:cubic-bezier(.4,0,.2,1)}.sidebar-left.is-collapsed{margin-left:calc(var(--gutter-width)*-1)}.sidebar-right.is-collapsed{margin-right:calc(var(--gutter-width)*-1)}.sidebar.is-collapsed>*,.sidebar.is-expanded.is-animating>*{display:none}.sidebar.is-collapsed>.sidebar-gutter{display:flex!important}.sidebar-gutter{bottom:0;left:100%;position:absolute;top:0;width:var(--gutter-width)}.sidebar-right>.sidebar-gutter{left:auto;right:100%}.is-collapsed>.gutter>.gutter-toggle>span{--tw-rotate:-180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sidebar-gutter:before{border-radius:var(--radius-full);opacity:0}.sidebar-gutter:hover:before{opacity:1;width:.5rem}.is-expanded .sidebar-gutter.gutter-border:before{opacity:1}.is-sidebar-resizing{cursor:col-resize}:root{--table-head-bg:var(--color-surface);--table-striped-color:var(--color-gray-50);--table-hover-color:rgba(var(--color-primary-500-rgb),.05);--table-border-color:var(--color-border)}.table{border-color:var(--table-border-color);width:100%}.table>thead{background-color:var(--table-head-bg)}.table>*>tr{border-bottom-width:1px}.table>*>tr>*{padding:.5rem 1rem}.table-striped>tbody>tr:nth-child(2n){background:var(--table-striped-color)}.table-hover>tbody>tr:hover>*{background:var(--table-hover-color)}.table.bordered>*>tr>*{border-width:1px}.table.borderless>*>tr,.table.borderless>*>tr>*{border-style:none}.condensed>*>tr>*{padding:.375rem .75rem}.table-fixed{table-layout:fixed}.table-fixed>*>tr>*{overflow:hidden;white-space:nowrap}.toolbar{align-items:center;display:flex;position:relative}.toolbar>.divider{margin:.5rem;width:1px;--tw-bg-opacity:1;background-color:rgba(var(--color-border-rgb),var(--tw-bg-opacity))}.toolbar-space{flex:1 1 auto;height:1px;width:1px}.toolbar>.dropdown.show{align-items:center;display:flex;position:relative}:root{--tooltip-bg:var(--skin-color,var(--color-inverse));--tooltip-text:var(--skin-text,var(--color-canvas));--tooltip-arrow:6px;--tooltip-delay:0.5s}.hint,[class*=" hint-"],[class^=hint-]{position:relative}.hint:before,[class*=" hint-"]:before,[class^=hint-]:before{border-style:solid;height:0;position:absolute;width:0;--tw-content:"";border-color:var(--tooltip-bg) transparent transparent transparent;border-width:var(--tooltip-arrow) var(--tooltip-arrow) 0 var(--tooltip-arrow);content:var(--tw-content);margin-bottom:calc(0px - var(--tooltip-arrow))}.hint:after,[class*=" hint-"]:after,[class^=hint-]:after{background-color:var(--tooltip-bg);border-color:var(--tooltip-color);border-radius:var(--radius);color:var(--tooltip-text);font-size:.75rem;line-height:1rem;max-width:28rem;overflow:hidden;padding:.25rem .5rem;position:absolute;white-space:nowrap;--tw-shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 3px 8px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-content:attr(data-hint);content:var(--tw-content);text-shadow:0 1px 0 var(--tooltip-bg)}.hint:after,.hint:before,[class*=" hint-"]:after,[class*=" hint-"]:before,[class^=hint-]:after,[class^=hint-]:before{bottom:100%;left:50%;opacity:0;pointer-events:none;transition-delay:0s;transition-duration:.15s;transition-property:opacity,visibility;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,1,1);visibility:hidden;z-index:1800;--tw-translate-x:-50%;--tw-translate-y:calc(var(--tooltip-arrow)*-1);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hint:hover:after,.hint:hover:before,[class*=" hint-"]:hover:after,[class*=" hint-"]:hover:before,[class^=hint-]:hover:after,[class^=hint-]:hover:before{opacity:1;transition-delay:var(--tooltip-delay);visibility:visible}.hint-bottom:before{border-color:transparent transparent var(--tooltip-bg) transparent;border-width:0 var(--tooltip-arrow) var(--tooltip-arrow) var(--tooltip-arrow);margin-bottom:auto;margin-top:calc(0px - var(--tooltip-arrow))}.hint-bottom:after,.hint-bottom:before{bottom:auto;top:100%;--tw-translate-y:var(--tooltip-arrow);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hint-left:before{border-color:transparent transparent transparent var(--tooltip-bg);border-width:var(--tooltip-arrow) 0 var(--tooltip-arrow) var(--tooltip-arrow);margin-bottom:auto;margin-right:calc(0px - var(--tooltip-arrow))}.hint-left:after,.hint-left:before{bottom:50%;left:auto;right:100%;--tw-translate-x:calc(var(--tooltip-arrow)*-1);--tw-translate-y:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hint-right:before{border-color:transparent var(--tooltip-bg) transparent;border-width:var(--tooltip-arrow) var(--tooltip-arrow) var(--tooltip-arrow) 0;margin-bottom:auto;margin-left:calc(0px - var(--tooltip-arrow))}.hint-right:after,.hint-right:before{bottom:50%;left:100%;--tw-translate-x:var(--tooltip-arrow);--tw-translate-y:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hint-lg:after,.hint-sm +/*! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com*/*,:after,:before{border-color:rgb(var(--color-gray-200-rgb));border-style:solid;border-width:0;box-sizing:border-box}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:-apple-system,Noto Sans,Helvetica Neue,Helvetica,Nimbus Sans L,Arial,Liberation Sans,PingFang SC,Hiragino Sans GB,Noto Sans CJK SC,Source Han Sans SC,Source Han Sans CN,Microsoft YaHei,Wenquanyi Micro Hei,WenQuanYi Zen Hei,ST Heiti,SimHei,WenQuanYi Zen Hei Sharp,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-tap-highlight-color:transparent}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-size:1em;font-variation-settings:normal}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-feature-settings:inherit;font-size:100%;font-variation-settings:inherit;font-weight:inherit;letter-spacing:inherit;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:rgb(var(--color-gray-400-rgb));opacity:1}input::placeholder,textarea::placeholder{color:rgb(var(--color-gray-400-rgb));opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}:root{--screen-2xl:1720px;--color-gray-50:#fcfdfe;--color-gray-100:#eff1f3;--color-gray-200:#ebedf3;--color-gray-300:#d2d6e5;--color-gray-400:#b2b9c5;--color-gray-500:#64758b;--color-gray-600:#4a577f;--color-gray-700:#3d4667;--color-gray-800:#353d57;--color-gray-900:#30394a;--color-gray-950:#0b0f18;--color-gray-50-rgb:252,253,254;--color-gray-100-rgb:239,241,243;--color-gray-200-rgb:235,237,243;--color-gray-300-rgb:210,214,229;--color-gray-400-rgb:178,185,197;--color-gray-500-rgb:100,117,139;--color-gray-600-rgb:74,87,127;--color-gray-700-rgb:61,70,103;--color-gray-800-rgb:53,61,87;--color-gray-900-rgb:48,57,74;--color-gray-950-rgb:11,15,24;--color-primary-50:#eef6ff;--color-primary-100:#d9eaff;--color-primary-200:#bcdcff;--color-primary-300:#8ec6ff;--color-primary-400:#59a5ff;--color-primary-500:#3883fa;--color-primary-600:#2b67e5;--color-primary-700:#144ce1;--color-primary-800:#173db6;--color-primary-900:#19388f;--color-primary-950:#142457;--color-primary-50-rgb:238,246,255;--color-primary-100-rgb:217,234,255;--color-primary-200-rgb:188,220,255;--color-primary-300-rgb:142,198,255;--color-primary-400-rgb:89,165,255;--color-primary-500-rgb:56,131,250;--color-primary-600-rgb:43,103,229;--color-primary-700-rgb:20,76,225;--color-primary-800-rgb:23,61,182;--color-primary-900-rgb:25,56,143;--color-primary-950-rgb:20,36,87;--color-secondary-50:#e7f6ff;--color-secondary-100:#cdecff;--color-secondary-200:#9bd9ff;--color-secondary-300:#77cbff;--color-secondary-400:#60c2ff;--color-secondary-500:#37b2fe;--color-secondary-600:#18a6fd;--color-secondary-700:#1099ed;--color-secondary-800:#078ada;--color-secondary-900:#046bab;--color-secondary-950:#062a4b;--color-secondary-50-rgb:231,246,255;--color-secondary-100-rgb:205,236,255;--color-secondary-200-rgb:155,217,255;--color-secondary-300-rgb:119,203,255;--color-secondary-400-rgb:96,194,255;--color-secondary-500-rgb:55,178,254;--color-secondary-600-rgb:24,166,253;--color-secondary-700-rgb:16,153,237;--color-secondary-800-rgb:7,138,218;--color-secondary-900-rgb:4,107,171;--color-secondary-950-rgb:6,42,75;--color-success-50:#effaf6;--color-success-100:#d6f2e7;--color-success-200:#ade5ce;--color-success-300:#83d7b6;--color-success-400:#5aca9d;--color-success-500:#31bd85;--color-success-600:#2ba675;--color-success-700:#28996c;--color-success-800:#21805a;--color-success-900:#185e3b;--color-success-950:#0e3421;--color-success-50-rgb:239,250,246;--color-success-100-rgb:214,242,231;--color-success-200-rgb:173,229,206;--color-success-300-rgb:131,215,182;--color-success-400-rgb:90,202,157;--color-success-500-rgb:49,189,133;--color-success-600-rgb:43,166,117;--color-success-700-rgb:40,153,108;--color-success-800-rgb:33,128,90;--color-success-900-rgb:24,94,59;--color-success-950-rgb:14,52,33;--color-warning-50:#fff4ea;--color-warning-100:#ffecdb;--color-warning-200:#ffd2a9;--color-warning-300:#ffbc7e;--color-warning-400:#ffaf65;--color-warning-500:#ff9f46;--color-warning-600:#f38f19;--color-warning-700:#ed8307;--color-warning-800:#d17100;--color-warning-900:#af5f01;--color-warning-950:#441506;--color-warning-50-rgb:255,244,234;--color-warning-100-rgb:255,236,219;--color-warning-200-rgb:255,210,169;--color-warning-300-rgb:255,188,126;--color-warning-400-rgb:255,175,101;--color-warning-500-rgb:255,159,70;--color-warning-600-rgb:243,143,25;--color-warning-700-rgb:237,131,7;--color-warning-800-rgb:209,113,0;--color-warning-900-rgb:175,95,1;--color-warning-950-rgb:68,21,6;--color-danger-50:#ffebeb;--color-danger-100:#ffd5d5;--color-danger-200:#ffb1b1;--color-danger-300:#ff9292;--color-danger-400:#ff7c7c;--color-danger-500:#fc5959;--color-danger-600:#fb2b2b;--color-danger-700:#d91b1b;--color-danger-800:#ba1313;--color-danger-900:#a20606;--color-danger-950:#470808;--color-danger-50-rgb:255,235,235;--color-danger-100-rgb:255,213,213;--color-danger-200-rgb:255,177,177;--color-danger-300-rgb:255,146,146;--color-danger-400-rgb:255,124,124;--color-danger-500-rgb:252,89,89;--color-danger-600-rgb:251,43,43;--color-danger-700-rgb:217,27,27;--color-danger-800-rgb:186,19,19;--color-danger-900-rgb:162,6,6;--color-danger-950-rgb:71,8,8;--color-important-50:#fff8f5;--color-important-100:#ffe2d9;--color-important-200:#ffcebf;--color-important-300:#ffb29b;--color-important-400:#ff8c68;--color-important-500:#ff8058;--color-important-600:#ff6f42;--color-important-700:#ff4912;--color-important-800:#c83509;--color-important-900:#611700;--color-important-950:#440609;--color-important-50-rgb:255,248,245;--color-important-100-rgb:255,226,217;--color-important-200-rgb:255,206,191;--color-important-300-rgb:255,178,155;--color-important-400-rgb:255,140,104;--color-important-500-rgb:255,128,88;--color-important-600-rgb:255,111,66;--color-important-700-rgb:255,73,18;--color-important-800-rgb:200,53,9;--color-important-900-rgb:97,23,0;--color-important-950-rgb:68,6,9;--color-special-50:#efecfa;--color-special-100:#e4dff7;--color-special-200:#c4b6ff;--color-special-300:#a38cff;--color-special-400:#8a6ff5;--color-special-500:#8166ee;--color-special-600:#7055df;--color-special-700:#513baa;--color-special-800:#452da5;--color-special-900:#30216e;--color-special-950:#271560;--color-special-50-rgb:239,236,250;--color-special-100-rgb:228,223,247;--color-special-200-rgb:196,182,255;--color-special-300-rgb:163,140,255;--color-special-400-rgb:138,111,245;--color-special-500-rgb:129,102,238;--color-special-600-rgb:112,85,223;--color-special-700-rgb:81,59,170;--color-special-800-rgb:69,45,165;--color-special-900-rgb:48,33,110;--color-special-950-rgb:39,21,96;--color-inherit:inherit;--color-transparent:transparent;--color-current:currentColor;--color-black:#000;--color-white:#fff;--color-canvas:#fff;--color-canvas-light:#fff;--color-canvas-dark:#fcfdfe;--color-inverse:#000;--color-surface:#eff1f3;--color-surface-light:#fcfdfe;--color-surface-strong:#ebedf3;--color-fore:#30394a;--color-fore-in-light:#30394a;--color-fore-in-dark:#fff;--color-focus:#bcdcff;--color-link:#2b47a2;--color-link-hover:#144ce1;--color-link-visited:#6328a4;--color-link-active:#144ce1;--color-placeholder:#64758b;--color-border:#ebedf3;--color-border-strong:#d2d6e5;--color-border-light:#eff1f3;--color-black-rgb:0,0,0;--color-white-rgb:255,255,255;--color-canvas-rgb:255,255,255;--color-canvas-light-rgb:255,255,255;--color-canvas-dark-rgb:252,253,254;--color-inverse-rgb:0,0,0;--color-surface-rgb:239,241,243;--color-surface-light-rgb:252,253,254;--color-surface-strong-rgb:235,237,243;--color-fore-rgb:48,57,74;--color-fore-in-light-rgb:48,57,74;--color-fore-in-dark-rgb:255,255,255;--color-focus-rgb:188,220,255;--color-link-rgb:43,71,162;--color-link-hover-rgb:20,76,225;--color-link-visited-rgb:99,40,164;--color-link-active-rgb:20,76,225;--color-placeholder-rgb:100,117,139;--color-border-rgb:235,237,243;--color-border-strong-rgb:210,214,229;--color-border-light-rgb:239,241,243;--radius-none:0px;--radius-sm:0.0625rem;--radius:0.125rem;--radius-md:0.25rem;--radius-lg:0.375rem;--radius-xl:0.5rem;--radius-2xl:0.75rem;--radius-3xl:1rem;--radius-full:9999px;--shadow-sm:0 1px 2px 0 rgba(0,0,0,.04),0 0 2px -1px rgba(0,0,0,.01);--shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--shadow-md:0 2px 6px -1px rgba(0,0,0,.2),0 1px 4px -2px rgba(0,0,0,.15);--shadow-lg:0 5px 15px -3px rgba(0,0,0,.2),0 2px 6px -4px rgba(0,0,0,.15);--shadow-xl:0 10px 25px -5px rgba(0,0,0,.2),0 4px 10px -6px rgba(0,0,0,.15);--shadow-2xl:0 10px 50px -10px rgba(0,0,0,.4),0 8px 20px -10px rgba(0,0,0,.3);--shadow-inner:inset 0 2px 4px 0 rgba(0,0,0,.05);--shadow-none:none;--space:0.25rem;--font-size-article:0.875rem;--font-size-root:16px;--font-size-page-lineHeight:1.5384615385}.dark{--color-gray-50:#0b0f18;--color-gray-100:#30394a;--color-gray-200:#353d57;--color-gray-300:#3d4667;--color-gray-400:#4a577f;--color-gray-500:#64758b;--color-gray-600:#b2b9c5;--color-gray-700:#d2d6e5;--color-gray-800:#ebedf3;--color-gray-900:#eff1f3;--color-gray-950:#fcfdfe;--color-gray-50-rgb:11,15,24;--color-gray-100-rgb:48,57,74;--color-gray-200-rgb:53,61,87;--color-gray-300-rgb:61,70,103;--color-gray-400-rgb:74,87,127;--color-gray-500-rgb:100,117,139;--color-gray-600-rgb:178,185,197;--color-gray-700-rgb:210,214,229;--color-gray-800-rgb:235,237,243;--color-gray-900-rgb:239,241,243;--color-gray-950-rgb:252,253,254;--color-primary-50:#142457;--color-primary-100:#19388f;--color-primary-200:#173db6;--color-primary-300:#144ce1;--color-primary-400:#2b67e5;--color-primary-500:#3883fa;--color-primary-600:#59a5ff;--color-primary-700:#8ec6ff;--color-primary-800:#bcdcff;--color-primary-900:#d9eaff;--color-primary-950:#eef6ff;--color-primary-50-rgb:20,36,87;--color-primary-100-rgb:25,56,143;--color-primary-200-rgb:23,61,182;--color-primary-300-rgb:20,76,225;--color-primary-400-rgb:43,103,229;--color-primary-500-rgb:56,131,250;--color-primary-600-rgb:89,165,255;--color-primary-700-rgb:142,198,255;--color-primary-800-rgb:188,220,255;--color-primary-900-rgb:217,234,255;--color-primary-950-rgb:238,246,255;--color-secondary-50:#062a4b;--color-secondary-100:#046bab;--color-secondary-200:#078ada;--color-secondary-300:#1099ed;--color-secondary-400:#18a6fd;--color-secondary-500:#37b2fe;--color-secondary-600:#60c2ff;--color-secondary-700:#77cbff;--color-secondary-800:#9bd9ff;--color-secondary-900:#cdecff;--color-secondary-950:#e7f6ff;--color-secondary-50-rgb:6,42,75;--color-secondary-100-rgb:4,107,171;--color-secondary-200-rgb:7,138,218;--color-secondary-300-rgb:16,153,237;--color-secondary-400-rgb:24,166,253;--color-secondary-500-rgb:55,178,254;--color-secondary-600-rgb:96,194,255;--color-secondary-700-rgb:119,203,255;--color-secondary-800-rgb:155,217,255;--color-secondary-900-rgb:205,236,255;--color-secondary-950-rgb:231,246,255;--color-success-50:#0e3421;--color-success-100:#185e3b;--color-success-200:#21805a;--color-success-300:#28996c;--color-success-400:#2ba675;--color-success-500:#31bd85;--color-success-600:#5aca9d;--color-success-700:#83d7b6;--color-success-800:#ade5ce;--color-success-900:#d6f2e7;--color-success-950:#effaf6;--color-success-50-rgb:14,52,33;--color-success-100-rgb:24,94,59;--color-success-200-rgb:33,128,90;--color-success-300-rgb:40,153,108;--color-success-400-rgb:43,166,117;--color-success-500-rgb:49,189,133;--color-success-600-rgb:90,202,157;--color-success-700-rgb:131,215,182;--color-success-800-rgb:173,229,206;--color-success-900-rgb:214,242,231;--color-success-950-rgb:239,250,246;--color-warning-50:#441506;--color-warning-100:#af5f01;--color-warning-200:#d17100;--color-warning-300:#ed8307;--color-warning-400:#f38f19;--color-warning-500:#ff9f46;--color-warning-600:#ffaf65;--color-warning-700:#ffbc7e;--color-warning-800:#ffd2a9;--color-warning-900:#ffecdb;--color-warning-950:#fff4ea;--color-warning-50-rgb:68,21,6;--color-warning-100-rgb:175,95,1;--color-warning-200-rgb:209,113,0;--color-warning-300-rgb:237,131,7;--color-warning-400-rgb:243,143,25;--color-warning-500-rgb:255,159,70;--color-warning-600-rgb:255,175,101;--color-warning-700-rgb:255,188,126;--color-warning-800-rgb:255,210,169;--color-warning-900-rgb:255,236,219;--color-warning-950-rgb:255,244,234;--color-danger-50:#470808;--color-danger-100:#a20606;--color-danger-200:#ba1313;--color-danger-300:#d91b1b;--color-danger-400:#fb2b2b;--color-danger-500:#fc5959;--color-danger-600:#ff7c7c;--color-danger-700:#ff9292;--color-danger-800:#ffb1b1;--color-danger-900:#ffd5d5;--color-danger-950:#ffebeb;--color-danger-50-rgb:71,8,8;--color-danger-100-rgb:162,6,6;--color-danger-200-rgb:186,19,19;--color-danger-300-rgb:217,27,27;--color-danger-400-rgb:251,43,43;--color-danger-500-rgb:252,89,89;--color-danger-600-rgb:255,124,124;--color-danger-700-rgb:255,146,146;--color-danger-800-rgb:255,177,177;--color-danger-900-rgb:255,213,213;--color-danger-950-rgb:255,235,235;--color-important-50:#440609;--color-important-100:#611700;--color-important-200:#c83509;--color-important-300:#ff4912;--color-important-400:#ff6f42;--color-important-500:#ff8058;--color-important-600:#ff8c68;--color-important-700:#ffb29b;--color-important-800:#ffcebf;--color-important-900:#ffe2d9;--color-important-950:#fff8f5;--color-important-50-rgb:68,6,9;--color-important-100-rgb:97,23,0;--color-important-200-rgb:200,53,9;--color-important-300-rgb:255,73,18;--color-important-400-rgb:255,111,66;--color-important-500-rgb:255,128,88;--color-important-600-rgb:255,140,104;--color-important-700-rgb:255,178,155;--color-important-800-rgb:255,206,191;--color-important-900-rgb:255,226,217;--color-important-950-rgb:255,248,245;--color-special-50:#271560;--color-special-100:#30216e;--color-special-200:#452da5;--color-special-300:#513baa;--color-special-400:#7055df;--color-special-500:#8166ee;--color-special-600:#8a6ff5;--color-special-700:#a38cff;--color-special-800:#c4b6ff;--color-special-900:#e4dff7;--color-special-950:#efecfa;--color-special-50-rgb:39,21,96;--color-special-100-rgb:48,33,110;--color-special-200-rgb:69,45,165;--color-special-300-rgb:81,59,170;--color-special-400-rgb:112,85,223;--color-special-500-rgb:129,102,238;--color-special-600-rgb:138,111,245;--color-special-700-rgb:163,140,255;--color-special-800-rgb:196,182,255;--color-special-900-rgb:228,223,247;--color-special-950-rgb:239,236,250;--color-canvas:#0b0f18;--color-inverse:#fff;--color-surface:#353d57;--color-surface-light:#30394a;--color-surface-strong:#3d4667;--color-fore:#ebedf3;--color-focus:#173db6;--color-link:#59a5ff;--color-link-hover:#3883fa;--color-link-visited:#bcdcff;--color-link-active:#bcdcff;--color-placeholder:#4a577f;--color-border:#353d57;--color-border-strong:#3d4667;--color-border-light:#30394a;--color-canvas-rgb:11,15,24;--color-inverse-rgb:255,255,255;--color-surface-rgb:53,61,87;--color-surface-light-rgb:48,57,74;--color-surface-strong-rgb:61,70,103;--color-fore-rgb:235,237,243;--color-focus-rgb:23,61,182;--color-link-rgb:89,165,255;--color-link-hover-rgb:56,131,250;--color-link-visited-rgb:188,220,255;--color-link-active-rgb:188,220,255;--color-placeholder-rgb:74,87,127;--color-border-rgb:53,61,87;--color-border-strong-rgb:61,70,103;--color-border-light-rgb:48,57,74}.light-in-dark{--color-gray-50:#fcfdfe;--color-gray-100:#eff1f3;--color-gray-200:#ebedf3;--color-gray-300:#d2d6e5;--color-gray-400:#b2b9c5;--color-gray-500:#64758b;--color-gray-600:#4a577f;--color-gray-700:#3d4667;--color-gray-800:#353d57;--color-gray-900:#30394a;--color-gray-950:#0b0f18;--color-gray-50-rgb:252,253,254;--color-gray-100-rgb:239,241,243;--color-gray-200-rgb:235,237,243;--color-gray-300-rgb:210,214,229;--color-gray-400-rgb:178,185,197;--color-gray-500-rgb:100,117,139;--color-gray-600-rgb:74,87,127;--color-gray-700-rgb:61,70,103;--color-gray-800-rgb:53,61,87;--color-gray-900-rgb:48,57,74;--color-gray-950-rgb:11,15,24;--color-primary-50:#eef6ff;--color-primary-100:#d9eaff;--color-primary-200:#bcdcff;--color-primary-300:#8ec6ff;--color-primary-400:#59a5ff;--color-primary-500:#3883fa;--color-primary-600:#2b67e5;--color-primary-700:#144ce1;--color-primary-800:#173db6;--color-primary-900:#19388f;--color-primary-950:#142457;--color-primary-50-rgb:238,246,255;--color-primary-100-rgb:217,234,255;--color-primary-200-rgb:188,220,255;--color-primary-300-rgb:142,198,255;--color-primary-400-rgb:89,165,255;--color-primary-500-rgb:56,131,250;--color-primary-600-rgb:43,103,229;--color-primary-700-rgb:20,76,225;--color-primary-800-rgb:23,61,182;--color-primary-900-rgb:25,56,143;--color-primary-950-rgb:20,36,87;--color-secondary-50:#e7f6ff;--color-secondary-100:#cdecff;--color-secondary-200:#9bd9ff;--color-secondary-300:#77cbff;--color-secondary-400:#60c2ff;--color-secondary-500:#37b2fe;--color-secondary-600:#18a6fd;--color-secondary-700:#1099ed;--color-secondary-800:#078ada;--color-secondary-900:#046bab;--color-secondary-950:#062a4b;--color-secondary-50-rgb:231,246,255;--color-secondary-100-rgb:205,236,255;--color-secondary-200-rgb:155,217,255;--color-secondary-300-rgb:119,203,255;--color-secondary-400-rgb:96,194,255;--color-secondary-500-rgb:55,178,254;--color-secondary-600-rgb:24,166,253;--color-secondary-700-rgb:16,153,237;--color-secondary-800-rgb:7,138,218;--color-secondary-900-rgb:4,107,171;--color-secondary-950-rgb:6,42,75;--color-success-50:#effaf6;--color-success-100:#d6f2e7;--color-success-200:#ade5ce;--color-success-300:#83d7b6;--color-success-400:#5aca9d;--color-success-500:#31bd85;--color-success-600:#2ba675;--color-success-700:#28996c;--color-success-800:#21805a;--color-success-900:#185e3b;--color-success-950:#0e3421;--color-success-50-rgb:239,250,246;--color-success-100-rgb:214,242,231;--color-success-200-rgb:173,229,206;--color-success-300-rgb:131,215,182;--color-success-400-rgb:90,202,157;--color-success-500-rgb:49,189,133;--color-success-600-rgb:43,166,117;--color-success-700-rgb:40,153,108;--color-success-800-rgb:33,128,90;--color-success-900-rgb:24,94,59;--color-success-950-rgb:14,52,33;--color-warning-50:#fff4ea;--color-warning-100:#ffecdb;--color-warning-200:#ffd2a9;--color-warning-300:#ffbc7e;--color-warning-400:#ffaf65;--color-warning-500:#ff9f46;--color-warning-600:#f38f19;--color-warning-700:#ed8307;--color-warning-800:#d17100;--color-warning-900:#af5f01;--color-warning-950:#441506;--color-warning-50-rgb:255,244,234;--color-warning-100-rgb:255,236,219;--color-warning-200-rgb:255,210,169;--color-warning-300-rgb:255,188,126;--color-warning-400-rgb:255,175,101;--color-warning-500-rgb:255,159,70;--color-warning-600-rgb:243,143,25;--color-warning-700-rgb:237,131,7;--color-warning-800-rgb:209,113,0;--color-warning-900-rgb:175,95,1;--color-warning-950-rgb:68,21,6;--color-danger-50:#ffebeb;--color-danger-100:#ffd5d5;--color-danger-200:#ffb1b1;--color-danger-300:#ff9292;--color-danger-400:#ff7c7c;--color-danger-500:#fc5959;--color-danger-600:#fb2b2b;--color-danger-700:#d91b1b;--color-danger-800:#ba1313;--color-danger-900:#a20606;--color-danger-950:#470808;--color-danger-50-rgb:255,235,235;--color-danger-100-rgb:255,213,213;--color-danger-200-rgb:255,177,177;--color-danger-300-rgb:255,146,146;--color-danger-400-rgb:255,124,124;--color-danger-500-rgb:252,89,89;--color-danger-600-rgb:251,43,43;--color-danger-700-rgb:217,27,27;--color-danger-800-rgb:186,19,19;--color-danger-900-rgb:162,6,6;--color-danger-950-rgb:71,8,8;--color-important-50:#fff8f5;--color-important-100:#ffe2d9;--color-important-200:#ffcebf;--color-important-300:#ffb29b;--color-important-400:#ff8c68;--color-important-500:#ff8058;--color-important-600:#ff6f42;--color-important-700:#ff4912;--color-important-800:#c83509;--color-important-900:#611700;--color-important-950:#440609;--color-important-50-rgb:255,248,245;--color-important-100-rgb:255,226,217;--color-important-200-rgb:255,206,191;--color-important-300-rgb:255,178,155;--color-important-400-rgb:255,140,104;--color-important-500-rgb:255,128,88;--color-important-600-rgb:255,111,66;--color-important-700-rgb:255,73,18;--color-important-800-rgb:200,53,9;--color-important-900-rgb:97,23,0;--color-important-950-rgb:68,6,9;--color-special-50:#efecfa;--color-special-100:#e4dff7;--color-special-200:#c4b6ff;--color-special-300:#a38cff;--color-special-400:#8a6ff5;--color-special-500:#8166ee;--color-special-600:#7055df;--color-special-700:#513baa;--color-special-800:#452da5;--color-special-900:#30216e;--color-special-950:#271560;--color-special-50-rgb:239,236,250;--color-special-100-rgb:228,223,247;--color-special-200-rgb:196,182,255;--color-special-300-rgb:163,140,255;--color-special-400-rgb:138,111,245;--color-special-500-rgb:129,102,238;--color-special-600-rgb:112,85,223;--color-special-700-rgb:81,59,170;--color-special-800-rgb:69,45,165;--color-special-900-rgb:48,33,110;--color-special-950-rgb:39,21,96;--color-inherit:inherit;--color-transparent:transparent;--color-current:currentColor;--color-black:#000;--color-white:#fff;--color-canvas:#fff;--color-canvas-light:#fff;--color-canvas-dark:#fcfdfe;--color-inverse:#000;--color-surface:#eff1f3;--color-surface-light:#fcfdfe;--color-surface-strong:#ebedf3;--color-fore:#30394a;--color-fore-in-light:#30394a;--color-fore-in-dark:#fff;--color-focus:#bcdcff;--color-link:#2b47a2;--color-link-hover:#144ce1;--color-link-visited:#6328a4;--color-link-active:#144ce1;--color-placeholder:#64758b;--color-border:#ebedf3;--color-border-strong:#d2d6e5;--color-border-light:#eff1f3;--color-black-rgb:0,0,0;--color-white-rgb:255,255,255;--color-canvas-rgb:255,255,255;--color-canvas-light-rgb:255,255,255;--color-canvas-dark-rgb:252,253,254;--color-inverse-rgb:0,0,0;--color-surface-rgb:239,241,243;--color-surface-light-rgb:252,253,254;--color-surface-strong-rgb:235,237,243;--color-fore-rgb:48,57,74;--color-fore-in-light-rgb:48,57,74;--color-fore-in-dark-rgb:255,255,255;--color-focus-rgb:188,220,255;--color-link-rgb:43,71,162;--color-link-hover-rgb:20,76,225;--color-link-visited-rgb:99,40,164;--color-link-active-rgb:20,76,225;--color-placeholder-rgb:100,117,139;--color-border-rgb:235,237,243;--color-border-strong-rgb:210,214,229;--color-border-light-rgb:239,241,243}body{--btn-bg:var(--color-gray-50)}@media (prefers-color-scheme:dark){.dark-auto{--color-gray-50:#0b0f18;--color-gray-100:#30394a;--color-gray-200:#353d57;--color-gray-300:#3d4667;--color-gray-400:#4a577f;--color-gray-500:#64758b;--color-gray-600:#b2b9c5;--color-gray-700:#d2d6e5;--color-gray-800:#ebedf3;--color-gray-900:#eff1f3;--color-gray-950:#fcfdfe;--color-gray-50-rgb:11,15,24;--color-gray-100-rgb:48,57,74;--color-gray-200-rgb:53,61,87;--color-gray-300-rgb:61,70,103;--color-gray-400-rgb:74,87,127;--color-gray-500-rgb:100,117,139;--color-gray-600-rgb:178,185,197;--color-gray-700-rgb:210,214,229;--color-gray-800-rgb:235,237,243;--color-gray-900-rgb:239,241,243;--color-gray-950-rgb:252,253,254;--color-primary-50:#142457;--color-primary-100:#19388f;--color-primary-200:#173db6;--color-primary-300:#144ce1;--color-primary-400:#2b67e5;--color-primary-500:#3883fa;--color-primary-600:#59a5ff;--color-primary-700:#8ec6ff;--color-primary-800:#bcdcff;--color-primary-900:#d9eaff;--color-primary-950:#eef6ff;--color-primary-50-rgb:20,36,87;--color-primary-100-rgb:25,56,143;--color-primary-200-rgb:23,61,182;--color-primary-300-rgb:20,76,225;--color-primary-400-rgb:43,103,229;--color-primary-500-rgb:56,131,250;--color-primary-600-rgb:89,165,255;--color-primary-700-rgb:142,198,255;--color-primary-800-rgb:188,220,255;--color-primary-900-rgb:217,234,255;--color-primary-950-rgb:238,246,255;--color-secondary-50:#062a4b;--color-secondary-100:#046bab;--color-secondary-200:#078ada;--color-secondary-300:#1099ed;--color-secondary-400:#18a6fd;--color-secondary-500:#37b2fe;--color-secondary-600:#60c2ff;--color-secondary-700:#77cbff;--color-secondary-800:#9bd9ff;--color-secondary-900:#cdecff;--color-secondary-950:#e7f6ff;--color-secondary-50-rgb:6,42,75;--color-secondary-100-rgb:4,107,171;--color-secondary-200-rgb:7,138,218;--color-secondary-300-rgb:16,153,237;--color-secondary-400-rgb:24,166,253;--color-secondary-500-rgb:55,178,254;--color-secondary-600-rgb:96,194,255;--color-secondary-700-rgb:119,203,255;--color-secondary-800-rgb:155,217,255;--color-secondary-900-rgb:205,236,255;--color-secondary-950-rgb:231,246,255;--color-success-50:#0e3421;--color-success-100:#185e3b;--color-success-200:#21805a;--color-success-300:#28996c;--color-success-400:#2ba675;--color-success-500:#31bd85;--color-success-600:#5aca9d;--color-success-700:#83d7b6;--color-success-800:#ade5ce;--color-success-900:#d6f2e7;--color-success-950:#effaf6;--color-success-50-rgb:14,52,33;--color-success-100-rgb:24,94,59;--color-success-200-rgb:33,128,90;--color-success-300-rgb:40,153,108;--color-success-400-rgb:43,166,117;--color-success-500-rgb:49,189,133;--color-success-600-rgb:90,202,157;--color-success-700-rgb:131,215,182;--color-success-800-rgb:173,229,206;--color-success-900-rgb:214,242,231;--color-success-950-rgb:239,250,246;--color-warning-50:#441506;--color-warning-100:#af5f01;--color-warning-200:#d17100;--color-warning-300:#ed8307;--color-warning-400:#f38f19;--color-warning-500:#ff9f46;--color-warning-600:#ffaf65;--color-warning-700:#ffbc7e;--color-warning-800:#ffd2a9;--color-warning-900:#ffecdb;--color-warning-950:#fff4ea;--color-warning-50-rgb:68,21,6;--color-warning-100-rgb:175,95,1;--color-warning-200-rgb:209,113,0;--color-warning-300-rgb:237,131,7;--color-warning-400-rgb:243,143,25;--color-warning-500-rgb:255,159,70;--color-warning-600-rgb:255,175,101;--color-warning-700-rgb:255,188,126;--color-warning-800-rgb:255,210,169;--color-warning-900-rgb:255,236,219;--color-warning-950-rgb:255,244,234;--color-danger-50:#470808;--color-danger-100:#a20606;--color-danger-200:#ba1313;--color-danger-300:#d91b1b;--color-danger-400:#fb2b2b;--color-danger-500:#fc5959;--color-danger-600:#ff7c7c;--color-danger-700:#ff9292;--color-danger-800:#ffb1b1;--color-danger-900:#ffd5d5;--color-danger-950:#ffebeb;--color-danger-50-rgb:71,8,8;--color-danger-100-rgb:162,6,6;--color-danger-200-rgb:186,19,19;--color-danger-300-rgb:217,27,27;--color-danger-400-rgb:251,43,43;--color-danger-500-rgb:252,89,89;--color-danger-600-rgb:255,124,124;--color-danger-700-rgb:255,146,146;--color-danger-800-rgb:255,177,177;--color-danger-900-rgb:255,213,213;--color-danger-950-rgb:255,235,235;--color-important-50:#440609;--color-important-100:#611700;--color-important-200:#c83509;--color-important-300:#ff4912;--color-important-400:#ff6f42;--color-important-500:#ff8058;--color-important-600:#ff8c68;--color-important-700:#ffb29b;--color-important-800:#ffcebf;--color-important-900:#ffe2d9;--color-important-950:#fff8f5;--color-important-50-rgb:68,6,9;--color-important-100-rgb:97,23,0;--color-important-200-rgb:200,53,9;--color-important-300-rgb:255,73,18;--color-important-400-rgb:255,111,66;--color-important-500-rgb:255,128,88;--color-important-600-rgb:255,140,104;--color-important-700-rgb:255,178,155;--color-important-800-rgb:255,206,191;--color-important-900-rgb:255,226,217;--color-important-950-rgb:255,248,245;--color-special-50:#271560;--color-special-100:#30216e;--color-special-200:#452da5;--color-special-300:#513baa;--color-special-400:#7055df;--color-special-500:#8166ee;--color-special-600:#8a6ff5;--color-special-700:#a38cff;--color-special-800:#c4b6ff;--color-special-900:#e4dff7;--color-special-950:#efecfa;--color-special-50-rgb:39,21,96;--color-special-100-rgb:48,33,110;--color-special-200-rgb:69,45,165;--color-special-300-rgb:81,59,170;--color-special-400-rgb:112,85,223;--color-special-500-rgb:129,102,238;--color-special-600-rgb:138,111,245;--color-special-700-rgb:163,140,255;--color-special-800-rgb:196,182,255;--color-special-900-rgb:228,223,247;--color-special-950-rgb:239,236,250;--color-canvas:#0b0f18;--color-inverse:#fff;--color-surface:#353d57;--color-surface-light:#30394a;--color-surface-strong:#3d4667;--color-fore:#ebedf3;--color-focus:#173db6;--color-link:#59a5ff;--color-link-hover:#3883fa;--color-link-visited:#bcdcff;--color-link-active:#bcdcff;--color-placeholder:#4a577f;--color-border:#353d57;--color-border-strong:#3d4667;--color-border-light:#30394a;--color-canvas-rgb:11,15,24;--color-inverse-rgb:255,255,255;--color-surface-rgb:53,61,87;--color-surface-light-rgb:48,57,74;--color-surface-strong-rgb:61,70,103;--color-fore-rgb:235,237,243;--color-focus-rgb:23,61,182;--color-link-rgb:89,165,255;--color-link-hover-rgb:56,131,250;--color-link-visited-rgb:188,220,255;--color-link-active-rgb:188,220,255;--color-placeholder-rgb:74,87,127;--color-border-rgb:53,61,87;--color-border-strong-rgb:61,70,103;--color-border-light-rgb:48,57,74}}*,:after,:before{--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity))}:focus-visible{outline-color:transparent}*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }html{font-size:var(--font-size-root)}body{--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));font-size:.8125rem;line-height:1.5384615385}.dark,body{--tw-text-opacity:1;color:rgba(var(--color-fore-rgb),var(--tw-text-opacity))}:root{--font-h1-size:2.25rem;--article-p-space:0.5em;--article-heading-space:0.5em;--article-hr-space:1.5em;--article-cell-padding:0.25rem 0.75rem}a,a.disabled:hover{--tw-text-opacity:1;color:rgba(var(--color-link-rgb),var(--tw-text-opacity))}a:hover{--tw-text-opacity:1;color:rgba(var(--color-link-hover-rgb),var(--tw-text-opacity))}a:focus-visible{outline-color:rgb(var(--color-focus-rgb));outline-offset:2px;outline-width:2px}.code,code{background-color:rgba(var(--color-important-500-rgb),.1);border-radius:var(--radius);padding-left:.25rem;padding-right:.25rem;--tw-text-opacity:1;color:rgba(var(--color-important-500-rgb),var(--tw-text-opacity))}pre{font-size:.75rem;line-height:1rem;overflow:auto;padding:.5rem}pre code{background-color:transparent;color:inherit;padding:0}samp,var{font-weight:700}.blockquote,blockquote{border-left-width:4px;font-size:.875rem;line-height:1.3rem;padding-left:.75rem;text-wrap:balance}.blockquote figcaption,blockquote figcaption{font-size:.75rem;line-height:1rem;--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-weight:700;line-height:1.5;text-wrap:balance}.h1,h1{font-size:1.75rem}.h2,h2{font-size:1.5rem}.h3,h3{font-size:1.25rem}.h4,h4{font-size:1rem}.h5,h5{font-size:.875rem}.h6,h6{font-size:.75rem}ul{list-style-type:disc}ol,ul{padding-left:1.5em}ol{list-style-type:decimal}dt{font-weight:700}dd{padding-left:2em}.article{color:var(--font-size-article);-webkit-hyphens:auto;hyphens:auto;overflow-wrap:break-word}.article h1,.article h2,.article h3,.article h4{margin-bottom:var(--article-heading-space);margin-top:var(--article-heading-space)}.article audio,.article blockquote,.article figure,.article img,.article ol,.article p,.article table,.article ul,.article video{margin-bottom:var(--article-p-space);margin-top:var(--article-p-space)}.article p{min-height:1em}.article p:empty:before{content:" ";display:inline-block}.article hr{margin-bottom:var(--article-hr-space);margin-top:var(--article-hr-space)}.article table{border-width:1px}.article td,.article th{border-bottom-width:1px;padding:var(--article-cell-padding)}.article thead{background-color:var(--table-head-bg,var(--color-gray-100))}.article ol *,.article table *,.article ul *{margin-bottom:0;margin-top:0}.article pre{border-radius:var(--radius);border-width:1px;--tw-bg-opacity:1;background-color:rgba(var(--color-gray-50-rgb),var(--tw-bg-opacity))}:root{--alert-bg:var(--color-surface)}.alert{align-items:center;background-color:var(--alert-bg);border-radius:var(--radius);display:flex;gap:.75rem;padding:.5rem .75rem}.alert-content{display:flex;flex:1 1 auto;flex-direction:column;gap:1rem}.alert-close{margin-bottom:-.375rem;margin-right:-.5rem;margin-top:-.375rem}.alert-link{color:inherit;font-weight:700}.alert-link:hover{color:inherit;text-decoration-line:underline}.alert-heading{font-weight:700;margin:0}.alert-text{flex:1 1 auto;white-space:pre-line}.alert-actions{align-items:center;display:flex;gap:.5rem}:root{--breadcrumb-divider:"/";--breadcrumb-divider-color:var(--color-gray-500);--breadcrumb-color-active:var(--color-gray-500)}.breadcrumb{display:flex;gap:1.25rem;list-style-type:none;padding-left:0}.breadcrumb>li{position:relative}.breadcrumb>li+li:before{color:var(--breadcrumb-divider-color);content:var(--breadcrumb-divider);display:block;left:-1.25rem;position:absolute;text-align:center;width:1.25rem}.breadcrumb>.active{color:var(--breadcrumb-color-active)}:root{--btn-radius:var(--radius);--btn-bg:var(--color-surface);--btn-border-color:var(--color-gray-300);--btn-height:2rem}.btn{align-items:center;background-color:var(--btn-bg);border-radius:var(--btn-radius);display:inline-flex;gap:.375rem;height:var(--btn-height);justify-content:center;padding-left:.75rem;padding-right:.75rem;white-space:nowrap;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:var(--btn-border-color)}.btn,.btn-default:hover{color:inherit}.btn-link{--tw-text-opacity:1;color:rgba(var(--color-link-rgb),var(--tw-text-opacity));text-decoration-line:underline;text-underline-offset:2px;--tw-ring-color:transparent}.btn-link:visited{color:rgba(var(--color-link-visited-rgb),var(--tw-text-opacity))}.btn-link:hover{--tw-text-opacity:1;color:rgba(var(--color-link-hover-rgb),var(--tw-text-opacity))}.btn-link{--btn-bg:transparent}.btn.size-xs,.size-xs>.btn{padding-left:.25rem;padding-right:.25rem;--btn-height:1.25rem}.btn.size-sm,.btn.size-xs,.size-sm>.btn,.size-xs>.btn{font-size:.75rem;line-height:1rem}.btn.size-sm,.size-sm>.btn{--btn-height:1.5rem}.btn.size-md,.btn.size-sm,.size-md>.btn,.size-sm>.btn{padding-left:.5rem;padding-right:.5rem}.btn.size-md,.size-md>.btn{--btn-height:1.75rem}.btn.size-lg,.size-lg>.btn{font-size:1rem;line-height:1.5rem;padding-left:1rem;padding-right:1rem;--btn-height:2.5rem}.btn.size-xl,.size-xl>.btn{font-size:1.125rem;line-height:1.75rem;padding-left:1.25rem;padding-right:1.25rem;--btn-height:3rem}.btn.square{aspect-ratio:1/1;gap:.125rem;padding-left:0;padding-right:0}@supports not (aspect-ratio:1/1){.btn.square{width:var(--btn-height)}}.btn.btn-caret{padding-left:.25rem;padding-right:.25rem}.btn.btn-wide{min-width:80px}:root{--checkbox-size:12px;--checkbox-color:var(--color-primary-500)}.checkbox,.radio{align-items:center;cursor:pointer;display:flex;gap:.375rem;min-height:20px}.checkbox>input[type=checkbox],.radio>input[type=radio]{accent-color:rgb(var(--color-primary-500-rgb));border-radius:var(--radius-lg)}.checkbox-primary,.radio-primary{display:flex;gap:.375rem;min-height:20px;position:relative}.checkbox-primary>input[type=checkbox],.radio-primary>input[type=radio]{bottom:0;left:0;opacity:0;position:absolute;right:0;top:0}.checkbox-primary>label,.radio-primary>label{cursor:pointer;padding-left:calc(var(--checkbox-size) + 8px);position:relative}.checkbox-primary>label:after,.checkbox-primary>label:before,.radio-primary>label:after,.radio-primary>label:before{display:block;height:var(--checkbox-size);left:0;position:absolute;transition-duration:.15s;transition-property:transform,background-color,color,opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--checkbox-size);--tw-content:"";content:var(--tw-content);top:calc((20px - var(--checkbox-size))/2)}.checkbox-primary>label:before{border-radius:var(--radius-sm);border-width:1px;--tw-border-opacity:1;border-color:rgba(var(--color-gray-400-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.checkbox-primary>label:after{--tw-scale-x:.9;--tw-scale-y:.9;background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iI2ZmZiIgZD0ibTkgMjAuNDItNi4yMS02LjIxIDIuODMtMi44M0w5IDE0Ljc3bDkuODgtOS44OSAyLjgzIDIuODN6Ii8+PC9zdmc+");transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.radio-primary>label:before{border-radius:var(--radius-full);border-width:1px;--tw-border-opacity:1;border-color:rgba(var(--color-gray-400-rgb),var(--tw-border-opacity))}.radio-primary>label:after{background-color:var(--checkbox-color);border-radius:var(--radius-full);opacity:0;--checkbox-radio-size:calc(var(--checkbox-size)*2/3);height:var(--checkbox-radio-size);left:calc((var(--checkbox-size) - var(--checkbox-radio-size))/2);top:calc((20px - var(--checkbox-radio-size))/2);width:var(--checkbox-radio-size)}.checkbox-primary>label:hover:before,.radio-primary>label:hover:before{border-color:var(--checkbox-color)}.checkbox-primary.focus>label:before,.checkbox-primary>input[type=checkbox]:focus+label:before,.radio-primary.focus>label:before,.radio-primary>input[type=radio]:focus+label:before{border-color:var(--checkbox-color);outline-color:rgb(var(--color-focus-rgb));outline-style:solid;outline-width:2px}.checkbox-primary.checked>label:before,.checkbox-primary>input[type=checkbox]:checked+label:before{background-color:var(--checkbox-color);border-color:var(--checkbox-color)}.radio-primary.checked>label:before,.radio-primary>input[type=radio]:checked+label:before{border-color:var(--checkbox-color)}.checkbox-primary.checked>label:after,.checkbox-primary>input[type=checkbox]:checked+label:after,.radio-primary.checked>label:after,.radio-primary>input[type=radio]:checked+label:after{opacity:1}.checkbox-primary.disabled>label,.checkbox-primary>input[type=checkbox]:disabled+label,.radio-primary.disabled>label,.radio-primary>input[type=radio]:disabled+label{cursor:not-allowed;opacity:.7;pointer-events:none;--tw-grayscale:grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.checkbox-primary.indeterminate>label:after,.checkbox-primary>input[type=checkbox]:indeterminate+label:after{--tw-border-opacity:1;background-color:var(--checkbox-color);background-image:none;border-color:rgba(var(--color-canvas-rgb),var(--tw-border-opacity));border-width:calc((var(--checkbox-size) - 2px)/2) 2px;opacity:1}.check-list{align-items:flex-start;flex-direction:column;gap:.5rem}.check-list,.check-list-inline{display:flex;padding-bottom:.375rem;padding-top:.375rem}.check-list-inline{align-items:center;-moz-column-gap:1rem;column-gap:1rem;flex-direction:row;flex-wrap:wrap;row-gap:.5rem}.switch{display:flex;gap:.375rem;min-height:20px;padding-left:2.5rem;position:relative}.switch>input{bottom:0;left:0;opacity:0;position:absolute;right:0;top:0}.switch>label:after,.switch>label:before{border-radius:var(--radius-full);border-width:1px;height:1rem;left:0;position:absolute;top:.125rem;width:2rem;--tw-border-opacity:1;border-color:rgba(var(--color-border-strong-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity));transition-duration:.3s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);--tw-content:"";content:var(--tw-content)}.switch>label:before{cursor:pointer;--tw-shadow:inset 0 2px 4px 0 rgba(0,0,0,.05);--tw-shadow-colored:inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.switch>label:hover:before{--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity))}.switch>label:after{height:.75rem;left:.125rem;top:.25rem;width:.75rem;--tw-border-opacity:1;border-color:rgba(var(--color-canvas-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));--tw-shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 3px 8px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.switch>input:checked+label:before{background-color:var(--checkbox-color);border-color:rgba(var(--color-inverse-rgb),.2)}.switch>input:checked+label:after{color:var(--checkbox-color);left:18px}.switch.focus>label:before,.switch>input:focus+label:before{border-color:var(--checkbox-color);outline-color:rgb(var(--color-focus-rgb));outline-style:solid;outline-width:2px}.switch.disabled>label:before,.switch>input:disabled+label:before{cursor:not-allowed;opacity:.7;pointer-events:none;--tw-grayscale:grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}:root{--form-control-radius:var(--radius);--form-control-border:var(--color-border);--form-control-focus:var(--color-primary-500);--form-control-disabled:var(--color-surface);--form-control-readonly:var(--color-surface)}.dark{--form-control-border:var(--color-border-strong)}input::-moz-placeholder,textarea::-moz-placeholder{--tw-placeholder-opacity:1;color:rgba(var(--color-placeholder-rgb),var(--tw-placeholder-opacity))}input::placeholder,textarea::placeholder{--tw-placeholder-opacity:1;color:rgba(var(--color-placeholder-rgb),var(--tw-placeholder-opacity))}.form-control{border-radius:var(--form-control-radius);display:block;height:2rem;width:100%;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));outline-color:transparent;outline-style:solid;outline-width:1px;padding:.25rem .5rem;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:var(--form-control-border);transition-duration:.15s;transition-property:outline,box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1)}.form-control.focus,.form-control:focus{outline-color:var(--form-control-focus);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:rgba(var(--color-focus-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.3}.form-control.disabled,.form-control[disabled]{background-color:var(--form-control-disabled);opacity:1}.form-control.readonly,.form-control[readonly]{background-color:var(--form-control-readonly)}select{-webkit-appearance:none;background-image:url("data:image/gif;base64,R0lGODlhBwAEAIAAAMvQ2////yH5BAEAAAEALAAAAAAHAAQAAAIIhA+BGWoNWSgAOw==");background-position:right 8px top 50%;background-repeat:no-repeat;background-size:8px auto;padding-right:1rem}select.form-control[multiple]{background-image:none}textarea.form-control{height:auto;min-height:32px}input[type=file].form-control{padding:.125rem}input[type=file].form-control::-webkit-file-upload-button{-webkit-appearance:none;appearance:none;border-radius:inherit;border-style:none;cursor:pointer;height:1.75rem;padding-left:.5rem;padding-right:.5rem}input[type=file].form-control::-webkit-file-upload-button:hover{background-color:rgba(var(--color-black-rgb),.1)}.has-error .form-control,.has-error.form-control{--tw-ring-color:rgba(var(--color-danger-500-rgb),0.6)}.has-error .form-control.focus,.has-error .form-control:focus,.has-error.form-control.focus,.has-error.form-control:focus{outline-color:rgb(var(--color-danger-500-rgb));--tw-ring-color:rgba(var(--color-danger-500-rgb),0.1)}.has-warning .form-control,.has-warning.form-control{--tw-ring-color:rgba(var(--color-warning-500-rgb),0.6)}.has-warning .form-control.focus,.has-warning .form-control:focus,.has-warning.form-control.focus,.has-warning.form-control:focus{outline-color:rgb(var(--color-warning-500-rgb));--tw-ring-color:rgba(var(--color-warning-500-rgb),0.2)}.has-success .form-control,.has-success.form-control{--tw-ring-color:rgba(var(--color-success-500-rgb),0.6)}.has-success .form-control.focus,.has-success .form-control:focus,.has-success.form-control.focus,.has-success.form-control:focus{outline-color:rgb(var(--color-success-500-rgb));--tw-ring-color:rgba(var(--color-success-500-rgb),0.2)}.form-control.size-sm,.size-sm>.form-control{font-size:.75rem;height:1.5rem;line-height:1rem;padding-left:.5rem;padding-right:.5rem}.form-control.size-md,.size-md>.form-control{height:1.75rem;padding-left:.5rem;padding-right:.5rem}.form-control.size-lg,.size-lg>.form-control{font-size:1rem;height:2.5rem;line-height:1.5rem;padding-left:.75rem;padding-right:.75rem}:root{--input-group-addon-bg:var(--color-gray-50)}.input-group{align-items:stretch;display:flex}.input-group>.form-control,.input-group>.input-control{flex:1 1 auto;z-index:1}.input-group-addon{align-items:center;background-color:var(--input-group-addon-bg);border-radius:var(--form-control-radius);display:flex;flex:none;height:2rem;padding-left:.5rem;padding-right:.5rem;z-index:0;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:var(--form-control-border)}.size-sm>.input-group-addon{font-size:.75rem;height:1.5rem;line-height:1rem;padding-left:.5rem;padding-right:.5rem}.size-lg>.input-group-addon{font-size:1rem;height:2.5rem;line-height:1.5rem;padding-left:.75rem;padding-right:.75rem}.input-group-control{flex:1 1 auto}.input-group>*+*,.input-group>*+* .form-control{border-bottom-left-radius:var(--radius-none);border-top-left-radius:var(--radius-none)}.input-group>:not(:last-child),.input-group>:not(:last-child) .form-control{border-bottom-right-radius:var(--radius-none);border-top-right-radius:var(--radius-none)}.input-group .btn:focus-visible,.input-group .form-control:focus{z-index:2}:root{--form-tip-color:var(--color-gray-400);--form-label-color:var(--color-gray-700);--form-horz-label-width:6rem;--form-grid-gap-x:1.25rem;--form-grid-gap-y:.75rem}.form{flex-direction:column;gap:1rem}.form,.form-label{display:flex}.form-label{align-items:center;color:var(--form-label-color);flex-direction:row;gap:.25rem;height:2rem;overflow:hidden;position:relative;text-overflow:ellipsis;white-space:nowrap}.form-label.required:after{content:"*";display:inline-block;margin-left:.25rem;--tw-translate-y:0.125rem;--tw-scale-x:1.25;--tw-scale-y:1.25;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-text-opacity:1;color:rgba(var(--color-danger-500-rgb),var(--tw-text-opacity))}.no-required .form-label.required:after{display:none}.form-label-actions{flex:1 1 auto;justify-content:flex-end}.form-label>.checkbox-primary{margin-left:.75rem}.form-tip{color:var(--form-tip-color);display:block;margin-top:.25rem}.has-error .form-tip{--tw-text-opacity:1;color:rgba(var(--color-danger-500-rgb),var(--tw-text-opacity))}.has-warning .form-tip{--tw-text-opacity:1;color:rgba(var(--color-warning-500-rgb),var(--tw-text-opacity))}.has-success .form-tip{--tw-text-opacity:1;color:rgba(var(--color-success-500-rgb),var(--tw-text-opacity))}.form-actions{display:flex;flex-direction:row;gap:1rem;justify-content:center}.form-grid{--form-grid-gap-x-half:calc(var(--form-grid-gap-x)/2);--form-grid-gap-y-half:calc(var(--form-grid-gap-y)/2);align-items:flex-start;flex-direction:row;flex-wrap:wrap;gap:0}.form-grid .form-group{padding:var(--form-grid-gap-y-half) var(--form-grid-gap-x-half)}.form-grid .form-label.required:after{margin-left:.25rem;order:-9999}.form-grid .form-label .checkbox{margin-left:.75rem;margin-right:.75rem}.form-grid .has-wrap-after:after{display:block;width:100%;--tw-content:"";content:var(--tw-content)}.form-grid-wrap{width:100%}.form-grid-row{display:flex;flex-direction:row;padding-bottom:var(--form-grid-gap-y-half);padding-top:var(--form-grid-gap-y-half);position:relative;width:100%}.form-grid-col{padding-left:var(--form-grid-gap-x-half);padding-right:var(--form-grid-gap-x-half)}.form-grid .form-actions{gap:1.5rem;justify-content:center;padding:var(--form-grid-gap-y) var(--form-grid-gap-x-half);width:100%}.form-grid .form-actions.is-pinned{border-top-width:1px}.form-horz .form-row{align-items:flex-start;display:flex;flex-direction:row}.form-horz .form-group{align-items:flex-start;display:flex;flex:none;flex-direction:row;flex-wrap:wrap;min-height:32px;min-width:0;padding-left:var(--form-horz-label-width);position:relative}.form-horz .form-row .form-group{flex:1 1 auto}.form-horz .form-group[class*=" w-"],.form-horz .form-group[class^=w-]{flex:none}.form-horz .form-group>*{min-width:0}.form-horz .form-group.no-label{padding-left:0}.form-horz .form-group.no-label>.check-list-inline{padding-left:1rem;padding-right:1rem}.form-horz .form-label{justify-content:flex-end;left:0;padding-left:1rem;padding-right:.5rem;position:absolute;top:0;width:var(--form-horz-label-width)}.form-horz .form-label.required:after{margin-left:0;order:-9999}.form-horz .form-tip{width:100%}.form-horz .form-tip,.form-horz .input-control,.form-horz .input-group{flex:1 1 auto}.form .form-group-wrapper,.form-horz .form-group-wrapper{flex:1 1 auto;min-width:0;width:100%;z-index:1}.form .form-group-wrapper.flex-none,.form-horz .form-group-wrapper.flex-none{flex:none}.form fieldset>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.form fieldset{padding-bottom:1rem;padding-left:1rem}.form-grid fieldset>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.form legend{border-bottom-width:1px;display:flex;justify-content:flex-start;margin-left:-1rem;width:100%;--tw-border-opacity:1;border-bottom-color:rgba(var(--color-primary-100-rgb),var(--tw-border-opacity));width:calc(100% + 1rem)}.form-title{align-items:center;background-color:rgba(var(--color-primary-50-rgb),.5);display:flex;font-weight:700;height:1.75rem;padding-left:.75rem;padding-right:.75rem}:root{--input-control-fix-width-sm:2rem;--input-control-fix-width:4.375rem;--input-control-fix-width-lg:6.75rem}.input-control{position:relative;--input-control-prefix:8px;--input-control-suffix:8px}.input-control-prefix,.input-control-suffix{align-items:center;display:flex;height:100%;left:0;opacity:.5;padding-left:.5rem;padding-right:.5rem;position:absolute;top:0;white-space:nowrap;width:var(--input-control-prefix);z-index:1}.input-control-suffix{justify-content:flex-end;left:auto;right:0;width:var(--input-control-suffix)}.form-control:focus+.input-control-prefix,.form-control:focus+.input-control-suffix{opacity:1}.input-control>.form-control{padding-left:var(--input-control-prefix);padding-right:var(--input-control-suffix)}.input-control>select.form-control{background-position-x:calc(100% - var(--input-control-suffix) - 8px);padding-right:calc(var(--input-control-suffix) + 20px)}.has-prefix{--input-control-prefix:var(--input-control-fix-width)}.has-suffix{--input-control-suffix:var(--input-control-fix-width)}.has-prefix-sm{--input-control-prefix:var(--input-control-fix-width-sm)}.has-suffix-sm{--input-control-suffix:var(--input-control-fix-width-sm)}.has-prefix-lg{--input-control-prefix:var(--input-control-fix-width-lg)}.has-suffix-lg{--input-control-suffix:var(--input-control-fix-width-lg)}.has-prefix-icon{--input-control-prefix:32px}.has-suffix-icon{--input-control-suffix:32px}.input-control.has-prefix-icon{--input-control-prefix:32px}.input-control.has-suffix-icon{--input-control-suffix:32px}.input-control.has-prefix-icon>.input-control-prefix,.input-control.has-suffix-icon>.input-control-suffix{justify-content:center;padding:0}.input-control.size-sm>.form-control,.input-control.size-sm>.input-control-prefix,.input-control.size-sm>.input-control-suffix{font-size:.75rem;line-height:1rem}.input-control.size-lg>.form-control,.input-control.size-lg>.input-control-prefix,.input-control.size-lg>.input-control-suffix{font-size:1rem;line-height:1.5rem}:root{--label-bg:rgba(var(--color-canvas-rgb),.1);--label-border-color:var(--color-gray-300);--label-color:var(--color-gray-500);--label-radius:var(--radius)}.label{align-items:center;background-color:var(--label-bg);border-radius:var(--label-radius);color:var(--label-color);display:inline-flex;font-size:.75rem;height:1.25rem;justify-content:center;line-height:1rem;min-width:1.25rem;padding-left:.25rem;padding-right:.25rem;white-space:pre-line;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:var(--label-border-color)}.label.size-lg{font-size:.8125rem;height:1.5rem;line-height:1.25rem;min-width:1.5rem;padding-left:.5rem;padding-right:.5rem}.label.size-sm{height:1rem;min-width:1rem}.label-dot{aspect-ratio:1/1;border-radius:var(--radius-full);height:.5rem;min-width:0;padding-left:0;padding-right:0}.caret,.caret-down,.caret-left,.caret-right,.caret-up{align-items:center;display:inline-flex;height:.75rem;justify-content:center;position:relative;width:.75rem}.caret-down:before,.caret-left:before,.caret-right:before,.caret-up:before,.caret:before{display:block;height:.35rem;transform-origin:center;width:.35rem;--tw-translate-y:-1px;--tw-rotate:45deg;border-color:currentColor;border-width:0 1px 1px 0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-content:"";content:var(--tw-content)}.caret-left:before{--tw-translate-x:0.125rem;--tw-translate-y:0px;--tw-rotate:135deg}.caret-left:before,.caret-right:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.caret-right:before{--tw-translate-x:-0.125rem;--tw-translate-y:0px;--tw-rotate:-45deg}.caret-up:before{--tw-translate-y:0.125rem;--tw-rotate:225deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:focus>.caret,:hover>.caret{opacity:inherit}.disabled .caret,.readonly .caret{opacity:var(--caret-opacity,.5)}.listitem{align-items:center;border-radius:var(--radius);display:flex;flex-direction:row;flex-wrap:nowrap;gap:.5rem;padding:.25rem}.listitem.selected{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity))}.listitem.active{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.item-actions,.item-avatar,.item-icon,.item-trailing,.item-trailing-icon{flex:none}.item-icon.icon-EMPTY{display:none}.item-subtitle{font-size:.75rem;line-height:1rem;opacity:.8}.item-content,.item-text{flex:1 1 auto;min-width:0}.item-leading,.item-trailing{align-items:center;display:flex;flex:none;gap:.5rem}.item-checkbox{margin-right:-.5rem}.item-content{align-items:center;display:flex;gap:.5rem}.multiline>.item-content{align-items:flex-start;flex-direction:column;gap:.25rem}.list{list-style-type:none;padding-left:0}.list-heading-inner .item-title{font-weight:700;--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.list.is-nested-sub{padding-left:var(--list-indent)}.is-nested-collapsed{display:none}.nested-toggle-icon{align-items:center;border-radius:var(--radius);display:flex;height:1rem;justify-content:center;margin-right:-.25rem;width:1rem}.magnifier{display:inline-block;height:1em;position:relative;width:1em}.magnifier:after,.magnifier:before{border-radius:var(--radius-full);content:"";display:block;position:absolute}.magnifier:before{border:1px solid;height:.75em;width:.75em}.magnifier:after{height:1px;left:.55em;top:.7em;width:.5em;--tw-rotate:45deg;background-color:currentColor;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.close{aspect-ratio:1/1;display:block;position:relative;width:1em}.close:after,.close:before{content:"";display:block;height:1px;left:0;position:absolute;width:100%;--tw-rotate:45deg;background-color:currentColor;border-radius:var(--radius-full);top:calc(50% - .5px)}.close:after,.close:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.close:after{--tw-rotate:-45deg}.search-box.compact.has-prefix-icon{--input-control-prefix:24px}.search-box.compact.has-suffix-icon{--input-control-suffix:24px}:root{--menu-bg:var(--color-canvas);--menu-active-bg:var(--color-primary-500);--menu-active-color:var(--color-canvas);--menu-selected-bg:var(--color-primary-50);--menu-selected-color:var(--color-primary-500);--menu-icon-opacity:.5;--menu-icon-margin:1.75rem;--menu-min-width:3rem;--menu-heading-color:var(--color-gray-500)}.menu{background:var(--menu-bg);max-width:var(--menu-max-width);min-width:var(--menu-min-width);padding:.5rem}.menu-wrapper{display:flex;flex-direction:column;min-height:0}.menu-wrapper>.menu{flex:1 1 auto;min-height:0;overflow:auto}.menu-item>.item-inner,.menu-item>a{align-items:center;border-radius:var(--radius);color:inherit;cursor:pointer;display:flex;gap:.5rem;justify-content:space-between;overflow:hidden;padding:.25rem .5rem;position:relative;text-overflow:ellipsis;transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);white-space:nowrap}.menu-item>.selected{background:var(--menu-selected-bg);color:var(--menu-selected-color)}.menu-item>.active,.menu-item>a.hover,.menu-item>a:hover{background:var(--menu-active-bg);color:var(--menu-active-color)}.menu-item.disabled>a:hover,.menu-item>a.disabled:hover{background-color:inherit;color:inherit}.menu-item>a>.item-title{flex:1 1 auto}.menu>.divider{margin:.25rem}.menu-heading{align-items:center;color:var(--menu-heading-color);display:flex;font-size:.75rem;font-weight:700;height:2rem;line-height:1rem;padding-left:.25rem;padding-right:.25rem}.has-icons>.menu-item>a>.item-icon{align-items:center;display:flex;height:1rem;justify-content:center;opacity:var(--menu-icon-opacity);width:1rem}.menu-nested{--menu-indent:10px}.menu-nested .menu{background-color:transparent;margin-bottom:-.5rem;margin-left:var(--menu-indent);margin-top:-.5rem;padding-right:0}.menu-nested .menu-item>.item-inner{padding-left:0}.menu-nested .search-menu-footer,.menu-nested .search-menu-header{margin-bottom:.25rem;margin-left:var(--menu-indent);margin-top:-.125rem;padding-right:1px}.menu.compact .menu-item>a{padding-bottom:.125rem;padding-top:.125rem}.menu.compact .menu-heading{height:1.75rem}.search-menu-header{padding:.5rem .5rem .125rem}.search-menu-footer{padding:.125rem .5rem .5rem}.search-menu .item.is-not-match,.search-menu-footer:empty,.search-menu-header:empty{display:none}.search-menu .item.is-not-match.has-match-child{display:list-item}.search-menu .item.is-not-match.has-match-child>.item-inner{opacity:.5;pointer-events:none}.search-menu.no-toggle-on-search .nested-toggle-icon{pointer-events:none}.search-menu .is-match-keys{font-weight:700;text-decoration-line:underline}.search-menu-no-match-hint{display:none;font-size:.75rem;line-height:1rem;padding:.5rem;--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.no-match-child .search-menu-no-match-hint{display:block}.no-match-child>.menu{display:none}:root{--alert-bg:var(--color-surface)}.alert{align-items:center;background-color:var(--alert-bg);border-radius:var(--radius);display:flex;gap:.75rem;padding:.5rem .75rem}.alert-content{display:flex;flex:1 1 auto;flex-direction:column;gap:1rem}.alert-close{margin-bottom:-.375rem;margin-right:-.5rem;margin-top:-.375rem}.alert-link{color:inherit;font-weight:700}.alert-link:hover{color:inherit;text-decoration-line:underline}.alert-heading{font-weight:700;margin:0}.alert-text{flex:1 1 auto;white-space:pre-line}.alert-actions{align-items:center;display:flex;gap:.5rem}.close{aspect-ratio:1/1;display:block;position:relative;width:1em}.close:after,.close:before{content:"";display:block;height:1px;left:0;position:absolute;width:100%;--tw-rotate:45deg;background-color:currentColor;border-radius:var(--radius-full);top:calc(50% - .5px)}.close:after,.close:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.close:after{--tw-rotate:-45deg}.messagers{align-items:flex-end;bottom:0;display:flex;flex-direction:column;gap:.5rem;justify-content:flex-end;left:0;padding:1rem;pointer-events:none;position:fixed;right:0;top:0;z-index:1900}.messagers-top-left,.messagers-top-right{justify-content:flex-start}.messagers-bottom-left,.messagers-top-left{align-items:flex-start}.messagers-bottom,.messagers-center,.messagers-top{align-items:center}.messagers-top,.messagers-top-left,.messagers-top-right{justify-content:flex-start}.messagers-center{justify-content:center}.messager{background-color:rgba(var(--color-inverse-rgb),.8);pointer-events:auto;--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity));--tw-shadow:0 10px 25px -5px rgba(0,0,0,.2),0 4px 10px -6px rgba(0,0,0,.15);--tw-shadow-colored:0 10px 25px -5px var(--tw-shadow-color),0 4px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);transition-duration:.3s}:root{--progress-radius:var(--radius);--progress-striped-size:40px;--progress-bg:var(--color-surface);--progress-bar-color:var(--color-primary-500)}.progress{background:var(--progress-bg);border-radius:var(--progress-radius);display:flex;height:1.25rem}.progress-bar{background:var(--progress-bar-color);height:100%;transition-duration:.5s;transition-property:width;transition-timing-function:cubic-bezier(.4,0,.2,1)}.progress-striped>.progress-bar{background-image:linear-gradient(45deg,rgba(var(--color-canvas-rgb),.15) 25%,transparent 25%,transparent 50%,rgba(var(--color-canvas-rgb),.15) 50%,rgba(var(--color-canvas-rgb),.15) 75%,transparent 75%,transparent);background-size:var(--progress-striped-size) var(--progress-striped-size)}.progress>.progress-bar:first-child{border-bottom-left-radius:inherit;border-top-left-radius:inherit}.progress>.progress-bar:last-child{border-bottom-right-radius:inherit;border-top-right-radius:inherit}.progress.active .progress-bar{animation:progress-bar-stripes 2s linear infinite}@keyframes progress-bar-stripes{0%{background-position:40px 0}to{background-position:0 0}}.scrollbar,:root{--scrollbar-size:10px;--scrollbar-opacity:.6;--scrollbar-bg:rgba(var(--color-inverse-rgb),.15);--scrollbar-inset:inset 0 0 0 1px rgba(var(--color-inverse-rgb),.05);--scrollbar-bar-bg:rgba(var(--color-inverse-rgb),.3);--scrollbar-hover-bg:rgba(var(--color-inverse-rgb),.4);--scrollbar-drag-bg:rgba(var(--color-inverse-rgb),.5);--scrollbar-radius:var(--radius-sm);--scrollbar-duration:.7s}::-webkit-scrollbar{height:var(--scrollbar-size);width:var(--scrollbar-size)}::-webkit-scrollbar-track{box-shadow:var(--scrollbar-shadow);-webkit-transition:background-color var(--scrollbar-duration);transition:background-color var(--scrollbar-duration)}::-webkit-scrollbar-track:hover,:hover::-webkit-scrollbar-track{background:var(--scrollbar-bg)}::-webkit-scrollbar-thumb{background:var(--scrollbar-bar-bg);border-radius:var(--scrollbar-radius);min-height:var(--scrollbar-size);-webkit-transition:var(--scrollbar-duration);transition:var(--scrollbar-duration);-webkit-transition-property:background-color;transition-property:background-color}::-webkit-scrollbar-thumb:hover{background:var(--scrollbar-hover-bg)}::-webkit-scrollbar-thumb:active{background:var(--scrollbar-drag-bg)}@-moz-document url-prefix(){.scrollbar-thin{scrollbar-width:thin}}.scrollbar-thin::-webkit-scrollbar{height:calc(var(--scrollbar-size)/2);width:calc(var(--scrollbar-size)/2)}.scrollbar-hover::-webkit-scrollbar,.scrollbar-hover::-webkit-scrollbar-thumb,.scrollbar-hover::-webkit-scrollbar-track{visibility:hidden}.scrollbar-hover:hover::-webkit-scrollbar,.scrollbar-hover:hover::-webkit-scrollbar-thumb,.scrollbar-hover:hover::-webkit-scrollbar-track{visibility:visible}@font-face{font-family:ZentaoIcon;font-style:normal;font-weight:400;src:url(./icons/ZentaoIcon.eot?v=1.70);src:url(./icons/ZentaoIcon.woff?v=1.70) format("woff"),url(./icons/ZentaoIcon.ttf?v=1.70) format("truetype"),url(./icons/ZentaoIcon.svg?v=1.70#regular) format("svg")}.icon,[class*=" icon-"],[class^=icon-]{display:inline-block;font-display:block;font-family:ZentaoIcon;font-size:14px;font-style:normal;font-variant:normal;font-weight:400;line-height:1;text-transform:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon:before{display:inline-block;min-width:14px;text-align:center}.icon-xs:before{font-size:.7em}.icon-sm:before{font-size:.8em}.icon-lg:before{font-size:1.25em;vertical-align:-10%}.icon-2x{font-size:28px}.icon-3x{font-size:42px}.icon-4x{font-size:56px}.icon-5x{font-size:70px}.icon-run{position:relative}.icon-run:before{opacity:.5}.icon-run:after{bottom:0;content:"\ea07";display:block;left:0;position:absolute;right:0;text-align:center;top:0}.icon-guihua:before{content:"\ea02"}.icon-memory:before{content:"\e9fd"}.icon-cpu:before{content:"\e9fe"}.icon-pause-pure:before{content:"\e9ff"}.icon-exclamation-pure:before{content:"\ea00"}.icon-zentao:before{content:"\e901"}.icon-zentao-alt:before{content:"\e900"}.icon-safe:before{content:"\ea06"}.icon-ai:before{content:"\e983"}.icon-kanban:before{content:"\e997"}.icon-college:before{content:"\e9c8"}.icon-home:before,.icon-menu-my:before{content:"\e97a"}.icon-program:before{content:"\e9aa"}.icon-lightbulb-alt:before,.icon-product:before{content:"\e98f"}.icon-project:before,.icon-rocket:before{content:"\e99c"}.icon-run:before{content:"\e9a9"}.icon-run-alt:before{content:"\ea07"}.icon-test:before{content:"\e956"}.icon-devops:before,.icon-infinite:before{content:"\e9a3"}.icon-doc:before,.icon-menu-doc:before{content:"\e99b"}.icon-statistic:before{content:"\e999"}.icon-group:before,.icon-menu-users:before,.icon-persons:before,.icon-team:before{content:"\e97e"}.icon-assets:before,.icon-diamond:before{content:"\e9ae"}.icon-flow:before{content:"\e994"}.icon-backend:before,.icon-cog-outline:before{content:"\ea05"}.icon-help:before{content:"\e968"}.icon-download:before,.icon-import:before{content:"\e904"}.icon-export:before{content:"\e905"}.icon-lightbulb:before{content:"\e91c"}.icon-close:before{content:"\e936"}.icon-check:before{content:"\e5ca"}.icon-minus:before{content:"\e926"}.icon-expand-alt:before{content:"\e6f1"}.icon-collapse-alt:before{content:"\e6f2"}.icon-fullscreen:before{content:"\e96b"}.icon-star-empty:before{content:"\e94a"}.icon-star:before{content:"\e94b"}.icon-exclamation-sign:before{content:"\e930"}.icon-info-sign:before{content:"\e9d5"}.icon-flag:before{content:"\e937"}.icon-check-circle:before{content:"\e92f"}.icon-check-sign:before{content:"\e938"}.icon-chart-pie:before{content:"\e95b"}.icon-history:before{content:"\e95f"}.icon-pencil:before{content:"\e254"}.icon-search:before{content:"\e928"}.icon-restart:before{content:"\e95e"}.icon-cog:before{content:"\e93b"}.icon-chart-line:before{content:"\e95c"}.icon-bar-chart:before,.icon-chart-bar:before{content:"\e95d"}.icon-exchange:before{content:"\e927"}.icon-severity:before{content:"\e973"}.icon-book:before{content:"\f02d"}.icon-treemap-alt:before{content:"\e971"}.icon-severity-solid:before{content:"\e902"}.icon-chat-line:before{content:"\e998"}.icon-stack:before{content:"\e943"}.icon-cube:before{content:"\e967"}.icon-minus-sign:before{content:"\e939"}.icon-bars-sign:before{content:"\e93a"}.icon-chat:before,.icon-message:before{content:"\e940"}.icon-more:before{content:"\e744"}.icon-certificate:before{content:"\f0a3"}.icon-bell:before{content:"\e7f5"}.icon-columns:before{content:"\f0db"}.icon-envelope-o:before{content:"\e92a"}.icon-unfold-all:before{content:"\e931"}.icon-fold-all:before{content:"\e932"}.icon-bars:before{content:"\e948"}.icon-cards-view:before{content:"\e949"}.icon-ellipsis-v:before{content:"\e5d4"}.icon-spinner-indicator:before{content:"\e982"}.icon-up-circle:before{content:"\e92b"}.icon-right-circle:before{content:"\e92c"}.icon-down-circle:before{content:"\e92d"}.icon-left-circle:before{content:"\e92e"}.icon-angle-double-right:before{content:"\f101"}.icon-angle-down:before{content:"\e313"}.icon-angle-left:before{content:"\e314"}.icon-angle-right:before{content:"\e315"}.icon-angle-top:before{content:"\e316"}.icon-first-page:before{content:"\e5dc"}.icon-last-page:before{content:"\e5dd"}.icon-caret-down:before{content:"\f0d7"}.icon-caret-up:before{content:"\f0d8"}.icon-caret-left:before{content:"\f0d9"}.icon-caret-right:before{content:"\f0da"}.icon-sort:before{content:"\f0dc"}.icon-sort-down:before{content:"\f0dd"}.icon-sort-up:before{content:"\f0de"}.icon-arrow-up:before{content:"\e923"}.icon-arrow-down:before{content:"\e924"}.icon-arrow-left:before{content:"\e952"}.icon-arrow-right:before{content:"\e93e"}.icon-chevron-left:before{content:"\e934"}.icon-chevron-right:before{content:"\e935"}.icon-chevron-double-up:before{content:"\e959"}.icon-chevron-double-down:before{content:"\e95a"}.icon-folder-account:before{content:"\e942"}.icon-folder-move:before{content:"\e960"}.icon-folder-plus:before{content:"\e961"}.icon-folder-upload:before{content:"\e962"}.icon-folder-star:before{content:"\e963"}.icon-folder-edit:before{content:"\e964"}.icon-folder-download:before{content:"\e965"}.icon-folder-outline:before{content:"\e966"}.icon-folder:before{content:"\e944"}.icon-folder-o:before{content:"\e945"}.icon-folder-open-o:before{content:"\e946"}.icon-folder-open:before{content:"\e947"}.icon-color:before{content:"\e93c"}.icon-paper-clip:before{content:"\e93d"}.icon-text:before{content:"\e929"}.icon-share:before{content:"\f064"}.icon-format-list-bulleted:before{content:"\e9a8"}.icon-format-bold:before{content:"\e953"}.icon-format-header-pound:before{content:"\e954"}.icon-format-italic:before{content:"\e955"}.icon-format-list-numbers:before{content:"\e969"}.icon-format-quote-close:before{content:"\e96a"}.icon-image:before{content:"\e96c"}.icon-table-large:before{content:"\e96d"}.icon-productplan:before{content:"\e9fc"}.icon-aiux:before{content:"\e99e"}.icon-qc:before{content:"\e986"}.icon-qc-q:before{content:"\e985"}.icon-qc-c:before{content:"\e987"}.icon-sonarqube:before{content:"\e9ba"}.icon-board:before{content:"\ea15"}.icon-market:before{content:"\ea01"}.icon-ztool:before{content:"\e9c1"}.icon-contacts:before{content:"\e9c3"}.icon-chats:before{content:"\e9c4"}.icon-ops:before{content:"\e903"}.icon-menu-backend:before{content:"\e993"}.icon-feedback:before{content:"\e991"}.icon-oa:before{content:"\e9a1"}.icon-more-circle:before{content:"\e988"}.icon-controls:before{content:"\e995"}.icon-account:before{content:"\e992"}.icon-about:before,.icon-info:before{content:"\e996"}.icon-exclamation:before{content:"\e9fa"}.icon-exit:before{content:"\e99a"}.icon-theme:before{content:"\e9a0"}.icon-globe:before,.icon-lang:before{content:"\f0ac"}.icon-template:before{content:"\e9fb"}.icon-seal:before{content:"\e9f8"}.icon-menu-arrow-left:before{content:"\e9f0"}.icon-menu-arrow-right:before{content:"\e9f1"}.icon-add-directory:before{content:"\e9ef"}.icon-design:before{content:"\e9ec"}.icon-download-alt:before{content:"\e9ea"}.icon-blame:before{content:"\e9e0"}.icon-draft-edit:before{content:"\e9dc"}.icon-sub-review-user:before{content:"\e9db"}.icon-sub-review:before{content:"\e9df"}.icon-ztf:before{content:"\e9dd"}.icon-save:before{content:"\e9d8"}.icon-list-box:before{content:"\e9b4"}.icon-usecase:before{content:"\e99d"}.icon-code:before{content:"\e990"}.icon-summary:before{content:"\e9ad"}.icon-more-alt:before{content:"\e9a7"}.icon-customer:before{content:"\e9d9"}.icon-alarm:before{content:"\ea16"}.icon-treeview:before{content:"\ea0d"}.icon-or:before{content:"\ea0b"}.icon-manager:before{content:"\ea0c"}.icon-target:before{content:"\ea0a"}.icon-ipd:before{content:"\e9f9"}.icon-wiki-lib:before{content:"\e9f5"}.icon-annex-lib:before{content:"\e9f6"}.icon-interface-lib:before{content:"\e9f7"}.icon-has-authority-pack:before{content:"\e9f3"}.icon-without-authority-pack:before{content:"\e9f4"}.icon-data-structure:before{content:"\e9f2"}.icon-waterfallplus:before{content:"\e9ed"}.icon-agileplus:before{content:"\e9ee"}.icon-horn:before{content:"\e9eb"}.icon-resume:before{content:"\e9e9"}.icon-mirror:before{content:"\e9e6"}.icon-remote:before{content:"\e9e7"}.icon-moon:before{content:"\e9e8"}.icon-ticket:before{content:"\e9e1"}.icon-gantt-alt:before{content:"\e9e2"}.icon-tree:before{content:"\e9c9"}.icon-list:before{content:"\e9cb"}.icon-gantt:before{content:"\e9cc"}.icon-group-view:before{content:"\e9cd"}.icon-inherit-space:before{content:"\e9c2"}.icon-card-archive:before{content:"\e9b8"}.icon-col-archive:before{content:"\e9b9"}.icon-col-add-right:before{content:"\e9bb"}.icon-col-add-left:before{content:"\e9bc"}.icon-col-split:before{content:"\e9bd"}.icon-waterfall:before{content:"\e9a4"}.icon-manual:before{content:"\e98d"}.icon-lane:before{content:"\e9b1"}.icon-mobile:before{content:"\ea18"}.icon-clear:before{content:"\ea19"}.icon-db:before{content:"\ea17"}.icon-unpinned:before{content:"\ea08"}.icon-pinned:before{content:"\ea09"}.icon-support-ticket:before{content:"\ea04"}.icon-snap-house:before{content:"\e9e3"}.icon-wrench:before{content:"\e9e4"}.icon-desktop:before{content:"\e9e5"}.icon-back-circle:before{content:"\e9da"}.icon-back:before{content:"\e9d3"}.icon-shield:before{content:"\e9ca"}.icon-meh:before{content:"\e9ce"}.icon-frown:before{content:"\e9cf"}.icon-smile:before{content:"\e9d0"}.icon-unlock-solid:before{content:"\e9d1"}.icon-lock-solid:before{content:"\e9d2"}.icon-ver:before{content:"\e9c6"}.icon-publish:before,.icon-send:before{content:"\e9c7"}.icon-tag:before{content:"\e9be"}.icon-tag-lock:before{content:"\e9bf"}.icon-code-fork:before{content:"\f126"}.icon-branch-lock:before{content:"\e9c0"}.icon-groups:before{content:"\e9af"}.icon-thumbs-up:before{content:"\f087"}.icon-thumbs-down:before{content:"\f088"}.icon-thumbs-up-solid:before{content:"\e9d6"}.icon-thumbs-down-solid:before{content:"\e9d7"}.icon-hash:before,.icon-version:before{content:"\e9ab"}.icon-p-square:before{content:"\e97b"}.icon-video-play:before{content:"\e97f"}.icon-plus-solid-circle:before{content:"\e974"}.icon-minuse-solid-circle:before{content:"\e9b6"}.icon-s:before{content:"\e975"}.icon-c:before{content:"\e976"}.icon-t:before{content:"\e977"}.icon-guide:before{content:"\e978"}.icon-todo:before{content:"\e979"}.icon-plus:before{content:"\e925"}.icon-side-left:before{content:"\e9b3"}.icon-side-right:before{content:"\e9b2"}.icon-fullscreen-exit:before{content:"\e972"}.icon-alert:before{content:"\e99f"}.icon-undo:before{content:"\e93f"}.icon-redo:before{content:"\e9d4"}.icon-swap:before{content:"\e9b0"}.icon-chat-solid:before{content:"\e9b5"}.icon-clock:before{content:"\e97c"}.icon-cost:before{content:"\e97d"}.icon-pencil-alt:before{content:"\e984"}.icon-size-height:before{content:"\e9c5"}.icon-upload-file:before{content:"\ea03"}.icon-file-log:before{content:"\e9de"}.icon-rich-text:before{content:"\e913"}.icon-markdown:before{content:"\e916"}.icon-excel:before{content:"\e933"}.icon-text-link:before{content:"\e94d"}.icon-ppt:before{content:"\e957"}.icon-word:before{content:"\e958"}.icon-doc-lib:before{content:"\e96f"}.icon-file-empty:before,.icon-file:before{content:"\f016"}.icon-file-text:before{content:"\f0f6"}.icon-file-alt:before{content:"\f15b"}.icon-file-text-alt:before{content:"\f15c"}.icon-file-pdf:before{content:"\f1c1"}.icon-file-word:before{content:"\f1c2"}.icon-file-excel:before{content:"\f1c3"}.icon-file-powerpoint:before{content:"\f1c4"}.icon-file-image:before{content:"\f1c5"}.icon-file-archive:before{content:"\f1c6"}.icon-file-audio:before{content:"\f1c7"}.icon-file-video:before{content:"\f1c8"}.icon-file-code:before{content:"\f1c9"}.icon-multi-input:before{content:"\ea1a"}.icon-cell-input:before{content:"\ea0e"}.icon-input:before{content:"\ea0f"}.icon-checkbox:before{content:"\ea10"}.icon-radio:before{content:"\ea11"}.icon-transition:before{content:"\ea12"}.icon-add-sub-chapter:before{content:"\ea13"}.icon-add-chapter:before{content:"\ea14"}.icon-menu-collapse:before{content:"\e980"}.icon-menu-expand:before{content:"\e981"}.icon-estimate:before{content:"\e9ac"}.icon-sprint:before{content:"\e9a2"}.icon-shield-check:before{content:"\e9a5"}.icon-ok:before{content:"\e9a6"}.icon-printer:before{content:"\e906"}.icon-bullhorn:before{content:"\e910"}.icon-person:before{content:"\e941"}.icon-fields:before{content:"\e989"}.icon-trigger:before{content:"\e98a"}.icon-layout:before{content:"\e98b"}.icon-audit:before{content:"\e98c"}.icon-ban-circle:before,.icon-cancel:before{content:"\e951"}.icon-eye:before{content:"\e94e"}.icon-eye-off:before{content:"\e96e"}.icon-unlock:before{content:"\e94f"}.icon-lock:before,.icon-private:before{content:"\e950"}.icon-move:before{content:"\e94c"}.icon-hand-right:before{content:"\e907"}.icon-checked:before{content:"\e908"}.icon-off:before{content:"\e909"}.icon-play:before,.icon-start:before{content:"\e90a"}.icon-time:before{content:"\e90b"}.icon-edit:before{content:"\e90c"}.icon-trash:before{content:"\e90d"}.icon-link:before{content:"\e90e"}.icon-unlink:before{content:"\e90f"}.icon-bug:before{content:"\e911"}.icon-list-alt:before{content:"\e912"}.icon-alter:before,.icon-change:before{content:"\e970"}.icon-glasses:before,.icon-review:before{content:"\e914"}.icon-sitemap:before,.icon-testcase:before{content:"\e915"}.icon-pluses:before{content:"\e917"}.icon-report-list:before{content:"\e918"}.icon-active:before,.icon-magic:before{content:"\e919"}.icon-treemap:before{content:"\e91a"}.icon-confirm:before{content:"\e91b"}.icon-split:before{content:"\e98e"}.icon-calendar:before,.icon-delay:before{content:"\e91d"}.icon-pause:before{content:"\e91e"}.icon-ban:before{content:"\e91f"}.icon-plus-bold:before{content:"\e920"}.icon-copy:before{content:"\e921"}.icon-refresh:before{content:"\e922"}.icon-diff:before{content:"\e9b7"}.status-active,.status-asked,.status-normal,.status-unconfirmed,.status-wait{color:var(--color-status-normal,#313c52)}.status-active.status-issue,.status-checking,.status-commenting,.status-confirmed,.status-doing{color:#ff6f42}.status-blocked,.status-hangup,.status-pause,.status-suspended{color:#b89664}.status-clarify,.status-draft{color:#8166ee}.status-noreview,.status-reviewing,.status-wait.status-testcase{color:#18a6fd}.status-active.status-risk,.status-changed,.status-changing,.status-fail{color:#fb2b2b}.status-checked,.status-done,.status-pass,.status-replied,.status-resolved,.status-success{color:#0dbb7d}.status-cancel,.status-canceled,.status-investigate{color:var(--color-status-cancel,#838a9d)}.status-closed,.status-testtask.status-done{color:var(--color-status-closed,#9ea3b0)}.dark{--color-status-normal:#eff1f3;--color-status-cancel:#9ea3b0;--color-status-closed:#838a9d}@media (prefers-color-scheme:dark){.dark-auto{--color-status-normal:#eff1f3;--color-status-cancel:#9ea3b0;--color-status-closed:#838a9d}}.pri,[class*=" pri-"],[class^=pri-]{border-color:currentColor;border-radius:var(--radius-full);border-width:1px;display:inline-block;font-size:.75rem;height:1rem;line-height:1rem;line-height:14px;max-width:54px;min-width:16px;overflow:hidden;padding-left:.125rem;padding-right:.125rem;text-align:center;white-space:nowrap;--tw-text-opacity:1;color:rgba(var(--color-gray-400-rgb),var(--tw-text-opacity))}.pri-1{color:rgb(255 73 18/var(--tw-text-opacity))}.pri-1,.pri-2{--tw-text-opacity:1}.pri-2{color:rgba(var(--color-warning-600-rgb),var(--tw-text-opacity))}.pri-3{color:rgb(59 130 246/var(--tw-text-opacity))}.pri-3,.pri-4{--tw-text-opacity:1}.pri-4{color:rgb(149 211 224/var(--tw-text-opacity))}.severity{display:inline-block;font-size:.75rem;height:1rem;line-height:1rem;line-height:14px;max-width:54px;min-width:16px;overflow:hidden;padding-left:.125rem;padding-right:.125rem;position:relative;text-align:center;white-space:nowrap;--tw-text-opacity:1;color:rgba(var(--color-gray-400-rgb),var(--tw-text-opacity))}.severity:before{background-color:currentColor;background-repeat:no-repeat;height:100%;left:0;position:absolute;top:0;width:100%;z-index:0;--tw-content:attr(data-severity);content:var(--tw-content);-webkit-mask:url('data:image/svg+xml;charset=utf-8,');mask:url('data:image/svg+xml;charset=utf-8,');-webkit-mask-size:100% 100%;mask-size:100% 100%}.severity:after,.severity:before{display:inline-block}.severity:after{--tw-scale-x:.75;--tw-scale-y:.75;content:attr(data-severity);line-height:20px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.severity[data-severity="1"]{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.severity[data-severity="2"]{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity))}.severity[data-severity="3"]{--tw-text-opacity:1;color:rgb(250 174 26/var(--tw-text-opacity))}.severity[data-severity="4"]{--tw-text-opacity:1;color:rgb(184 150 100/var(--tw-text-opacity))}.severity[data-severity="5"]{--tw-text-opacity:1;color:rgb(158 163 176/var(--tw-text-opacity))}.severity-label{background-color:transparent}.severity-label:after,.severity-label:before{--tw-content:none;content:var(--tw-content)}.risk-high{color:rgb(220 38 38/var(--tw-text-opacity))}.risk-high,.risk-middle{--tw-text-opacity:1}.risk-middle{color:rgb(234 179 8/var(--tw-text-opacity))}.label.label-id{border-radius:var(--radius-full);flex:none;--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-300-rgb),var(--tw-ring-opacity));--label-bg:rgba(var(--color-canvas-rgb),.1)}.nav-feature{--nav-active-bg:var(--color-primary-100);--nav-active-color:var(--color-fore)}.nav-feature .nav-checkbox>.checkbox,.nav-feature .nav-checkbox>.checkbox-primary,.nav-feature .nav-item>a{align-items:center;border-radius:var(--radius);height:2rem;padding-left:.625rem;padding-right:.625rem}.nav-feature .nav-item>a>.icon{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.sortable-list .listitem{cursor:move;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));padding-left:.5rem;padding-right:.5rem}.sortable-list .sortable-ghost>.listitem{--tw-bg-opacity:1;background-color:rgba(var(--color-warning-50-rgb),var(--tw-bg-opacity))}.sortable-empty-shadow{height:1px!important;left:0!important;opacity:0!important;pointer-events:none!important;position:absolute!important;top:0!important;width:1px!important}:root{--avatar-radius:12.5%;--avatar-bg:var(--color-surface)}.avatar{align-items:center;aspect-ratio:1/1;background:var(--avatar-bg);border-radius:var(--avatar-radius);display:inline-flex;justify-content:center;overflow:hidden;width:2rem}.avatar>img{height:100%;margin:0;-o-object-fit:cover;object-fit:cover;width:100%}.avatar.size-xs{width:1.25rem}.avatar.size-sm,.avatar.size-xs{font-size:.75rem;line-height:1rem}.avatar.size-sm{width:1.5rem}.avatar.size-lg{font-size:1.5rem;line-height:2rem;width:3rem}.avatar.size-xl{font-size:2.25rem;line-height:2.5rem;width:5rem}.avatar-group{display:flex;gap:.625rem}.avatar-group>.avatar{box-shadow:0 0 0 1px var(--color-canvas)}.avatar-group>*+*{margin-left:-1rem}.avatar-group.size-xs>*+*{margin-left:-.75rem}.avatar-group.size-sm>*+*{margin-left:-.875rem}.avatar-group.size-lg>*+*{margin-left:-1.25rem}.avatar-group.size-xl>*+*{margin-left:-1.5rem}.btn-group{display:flex;gap:1px}.btn-group>.btn.active{z-index:1}.btn-group>.btn:focus-within,.btn-group>.btn:hover{z-index:2}.btn-group>.btn.disabled,.btn-group>.btn:disabled{z-index:0}.btn-group>.btn:not(:first-child){border-bottom-left-radius:var(--radius-none);border-left-color:rgba(var(--color-canvas-rgb),.2);border-left-width:1px;border-top-left-radius:var(--radius-none)}.btn-group>.btn:not(:first-child):before{left:-1px}.btn-group>.btn:not(:last-child){border-bottom-right-radius:var(--radius-none);border-top-right-radius:var(--radius-none)}.color-picker-heading{align-items:center;font-weight:700;justify-content:space-between;margin-bottom:-.25rem;padding:.5rem}.color-picker-heading,.color-picker-row{display:flex;flex-direction:row}.color-picker-row{flex-wrap:wrap;gap:.5rem;padding:.75rem}.color-picker-item{align-items:center;border-radius:var(--radius-full);display:flex;height:1.25rem;justify-content:center;padding-left:.125rem;padding-right:.125rem;width:1.25rem;--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity));--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.pick-container{left:0;pointer-events:none;position:absolute;top:0;z-index:1600}.pick-pop{display:flex;flex-direction:column;opacity:0;pointer-events:auto;position:absolute;transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);z-index:1200}.pick-pop.in{opacity:1}.date-picker-menu{--menu-bg:none}.date-picker-menu .mini-calendar{margin-top:-.75rem}.date-pick-menu-month,.date-pick-menu-years{display:flex;flex-direction:row;flex-wrap:wrap;gap:1px;max-height:237px;padding-bottom:.5rem;padding-left:.5rem;padding-right:.5rem}.date-pick-menu-years>.btn{width:2.5rem}.date-pick-menu-years>.btn.is-current{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-100-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.date-pick-menu-month>.btn.active,.date-pick-menu-years>.btn.active{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.date-pick-menu-month>.btn{width:3rem}.date-picker-menu-footer{border-top-width:1px;display:flex;flex-direction:row;padding:.25rem}.date-picker-menu-footer>.toolbar{flex:1 1 auto;justify-content:center}.date-picker-menu>.menu{border-right-width:1px}.date-picker-menu .menu-item{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.time-picker-menu{max-height:inherit}.time-picker-menu .menu-item>a{justify-content:center}.datetime-picker-menu{max-height:inherit}.mini-calendar{padding:.5rem}.mini-calendar>.row>.col{align-items:center;justify-content:center;width:14.2857%}.mini-calendar-day{height:2rem}.mini-calendar-day.disabled{pointer-events:none;--tw-bg-opacity:1;background-color:rgba(var(--color-gray-200-rgb),var(--tw-bg-opacity));opacity:.5}.mini-calendar-day>.btn{width:2rem}.mini-calendar-day.is-weekend>.btn{--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.mini-calendar-day.active>.btn,.mini-calendar-day.is-today>.btn{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-100-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.mini-calendar-day.selected>.btn{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.mini-calendar-day.is-out-month{opacity:.5}.i-time{border-color:currentColor;border-radius:50%;border-width:.08em;display:inline-block;height:1em;position:relative;width:1em}.i-time:after{border-color:currentColor;border-width:0 0 .08em .08em;height:.385em;left:.3846em;position:absolute;top:.08em;width:.385em;--tw-content:"";content:var(--tw-content)}.chevron-down,.chevron-left,.chevron-right,.chevron-up{align-items:center;display:inline-flex;height:1em;justify-content:center;position:relative;width:1em}.chevron-down:before,.chevron-left:before,.chevron-right:before,.chevron-up:before{display:block;height:.6923077em;transform-origin:center;width:.6923077em;--tw-translate-y:-0.125rem;--tw-rotate:45deg;border-color:currentColor;border-width:0 1px 1px 0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-content:"";content:var(--tw-content)}.chevron-left:before{--tw-translate-y:0px;--tw-translate-x:0.125rem;--tw-rotate:135deg}.chevron-left:before,.chevron-right:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.chevron-right:before{--tw-translate-y:0px;--tw-translate-x:-0.125rem;--tw-rotate:-45deg}.chevron-up:before{--tw-translate-y:0.125rem;--tw-rotate:225deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.i-calendar{border-radius:2px;display:inline-block;height:.79em;margin-top:.16em;position:relative;width:1em}.i-calendar,.i-calendar:after,.i-calendar:before{border-color:currentColor;border-width:.08em}.i-calendar:after,.i-calendar:before{position:absolute;--tw-content:"";content:var(--tw-content)}.i-calendar:before{border-bottom-width:0;left:.15em;top:.307em;width:.6em}.i-calendar:after{border-bottom-width:0;border-top-width:0;height:.3077em;left:.17em;top:-.154em;width:.538em}:root{--modal-radius:var(--radius-lg);--modal-bg:rgba(var(--color-inverse-rgb),.4);--modal-sm:37.5rem;--modal-base:50rem;--modal-lg:75rem;--modal-xl:90rem}.modal{align-items:center;background-color:var(--modal-bg);bottom:0;display:none;justify-content:center;left:0;overflow:hidden;position:fixed;right:0;top:0;z-index:1500}.modal.load-indicator:before{background-color:transparent;transition-property:none}.modal.loading:after{--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.modal.loading .modal-dialog{opacity:0!important}.modal.show{display:flex}.modal-no-backdrop{background-color:transparent;pointer-events:none}.modal-hide{opacity:0!important}.modal-dialog{background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));pointer-events:auto;position:relative;--tw-bg-opacity:0.9;--tw-shadow:0 10px 50px -10px rgba(0,0,0,.4),0 8px 20px -10px rgba(0,0,0,.3);--tw-shadow-colored:0 10px 50px -10px var(--tw-shadow-color),0 8px 20px -10px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.05;--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);border-radius:var(--modal-radius);width:var(--modal-base)}.modal-content,.modal-dialog{max-height:100vh}.modal-content{display:flex;flex-direction:column;height:100%}.loading>.modal-dialog>.modal-content{opacity:0;visibility:hidden}.close{aspect-ratio:1/1;display:block;position:relative;width:1em}.close:after,.close:before{content:"";display:block;height:1px;left:0;position:absolute;width:100%;--tw-rotate:45deg;background-color:currentColor;border-radius:var(--radius-full);top:calc(50% - .5px)}.close:after,.close:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.close:after{--tw-rotate:-45deg}.modal-header{align-items:center;display:flex;flex:none;flex-wrap:nowrap;gap:1rem;padding:1rem 1.25rem;position:relative}.modal-title{flex:1 1 auto;font-size:1rem;font-weight:700;line-height:1.5rem}.modal-actions{position:absolute;right:.75rem;top:.75rem;z-index:15}.modal-body{flex:1 1 auto;overflow:auto;padding:1rem 1.25rem}.modal-footer{align-items:center;display:flex;flex:none;gap:.75rem;padding:1.25rem}.modal-dialog.size-full,.modal-dialog[data-size=full]{border-radius:var(--radius-none);height:100vh;width:100vw}.modal-dialog.size-sm,.modal-dialog[data-size=sm]{width:var(--modal-sm)}.modal-dialog.size-lg,.modal-dialog[data-size=lg]{width:var(--modal-lg)}.modal-dialog.size-xl,.modal-dialog[data-size=xl]{width:var(--modal-xl)}.modal-iframe{flex:1 1 auto;width:100%}.modal-trans{opacity:0;transition-duration:.2s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.modal-trans.in{opacity:1}.modal-trans>.modal-dialog{transition-property:transform,opacity;--tw-scale-x:.95;--tw-scale-y:.95;opacity:0;transition-duration:.3s}.modal-trans.in>.modal-dialog,.modal-trans>.modal-dialog{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.modal-trans.in>.modal-dialog{--tw-scale-x:1;--tw-scale-y:1;opacity:1}.modal-alert .modal-footer{justify-content:flex-end}.modal-alert .modal-header{margin-bottom:-1rem}.modal-alert .modal-body{overflow-wrap:break-word;white-space:pre-line}.modal-alert .modal-actions+.modal-body{padding-right:3rem}.modal-alert .modal-header+.modal-actions+.modal-body{padding-right:1.25rem}.file-selector{gap:.5rem;width:100%}.file-selector,.file-selector-box{display:flex;flex-direction:column}.file-selector-box{border-style:dashed;border-width:1px;gap:0;height:auto;justify-content:center;position:relative;--tw-border-opacity:1;background-color:rgba(var(--color-primary-50-rgb),.3);border-color:rgba(var(--color-primary-400-rgb),var(--tw-border-opacity));padding:.5rem .75rem;--tw-ring-color:transparent;--state-color:rgba(var(--color-primary-500-rgb),.05)}.file-selector-box .text{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.is-dragging .file-selector-box{border-style:solid}.file-selector-btn{align-items:center;display:flex;gap:.5rem;position:relative}.is-dragging .file-selector-btn{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity))}.file-selector-input{-webkit-appearance:none;-moz-appearance:none;appearance:none;bottom:0;cursor:pointer;left:0;opacity:.1;position:absolute;right:0;top:0}.file-selector-tip{--tw-text-opacity:1;color:rgba(var(--color-gray-400-rgb),var(--tw-text-opacity))}.file-selector.is-mode-button{flex-direction:column-reverse}.file-selector-list.is-empty{display:none}.file-selector-item .item-avatar{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-50-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.file-selector-item .item-subtitle{--tw-text-opacity:1;color:rgba(var(--color-gray-400-rgb),var(--tw-text-opacity))}.file-selector-item .item-content{flex:none;margin-right:.25rem;max-width:100%}.file-selector-item>.listitem:hover{background-color:rgba(var(--color-gray-500-rgb),.05)}.file-selector-item.is-renaming .item-avatar+.item-content{max-width:calc(100% - 32px)}.file-selector-rename-text{max-width:100%;min-width:100px;position:relative}.file-selector-rename-text>div{overflow:hidden;pointer-events:none;visibility:hidden;white-space:nowrap}.file-selector-rename-input{bottom:0;left:0;position:absolute;right:0;top:0}.file-selector-grid{align-items:flex-start;border-color:transparent;border-radius:var(--radius);border-width:1px;display:flex;flex-direction:row;flex-wrap:wrap;gap:var(--file-selector-grid-gap);--tw-bg-opacity:1;background-color:rgba(var(--color-surface-light-rgb),var(--tw-bg-opacity));padding:.5rem}.file-selector-grid-item{height:var(--file-selector-grid-height)}.file-selector-grid-btn,.file-selector-grid-item{width:var(--file-selector-grid-width)}.file-selector-grid-btn{aspect-ratio:1/1;border-style:dashed;border-width:1px;display:flex;flex-direction:column;gap:0;height:auto;justify-content:center;--tw-border-opacity:1;background-color:rgba(var(--color-primary-50-rgb),.3);border-color:rgba(var(--color-primary-400-rgb),var(--tw-border-opacity));padding:.5rem;--tw-ring-color:transparent;--state-color:rgba(var(--color-primary-500-rgb),.05)}.file-selector-grid-btn .text{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.file-selector-grid-item .listitem{flex-direction:column;height:100%;padding:0;position:relative;width:100%}.file-selector-grid-item .item-avatar{border-radius:var(--radius);border-width:1px;width:100%;--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.file-selector-grid-item .item-avatar.has-icon>.icon{font-size:calc(var(--file-selector-grid-width)/2)}.file-selector-grid-item .item-content,.file-selector-grid-item .toolbar{aspect-ratio:1/1;left:0;position:absolute;right:0;top:0;width:100%}.file-selector-grid-item .item-title{bottom:-1.25rem;font-size:.75rem;left:0;line-height:1rem;overflow:hidden;position:absolute;right:1.5rem;white-space:nowrap}.file-selector-grid-item .item-subtitle{border-radius:var(--radius-full);bottom:.25rem;left:.25rem;position:absolute;--tw-bg-opacity:1;background-color:rgba(var(--color-fore-rgb),var(--tw-bg-opacity));font-size:.75rem;line-height:1rem;padding-left:.25rem;padding-right:.25rem;--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity));opacity:0;transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.file-selector-grid-item:hover .item-subtitle{opacity:1}.file-selector-grid-item .toolbar .btn[data-remove-file]{background-color:rgba(var(--color-inverse-rgb),.5);padding:.375rem;position:absolute;right:1px;top:1px;--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.file-selector-grid-item .toolbar .btn[data-rename-file]{bottom:-1.5rem;padding:0;position:absolute;right:0}.file-selector-grid .file-selector-rename{bottom:0;height:1.5rem;position:absolute;top:auto}.file-selector-grid .file-selector-rename-text{width:100%}.is-dragging .file-selector-grid{border-width:1px;--tw-border-opacity:1;border-color:rgba(var(--color-primary-400-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity))}:root{--nav-radius:var(--radius);--nav-active-color:var(--color-primary-500);--nav-active-bg:var(--color-inherit);--nav-heading-color:var(--color-gray-500);--nav-gap:0.25rem;--nav-item-color:inherit;--nav-item-bg:transparent;--nav-item-padding:1rem;--nav-item-height:2rem;--nav-item-gap:0.75rem;--nav-divider-height:calc(var(--nav-item-height)/2);--nav-divider-margin:0.5rem}.nav{list-style-type:none;padding-left:0}.nav,.nav-item{align-items:center;display:flex;position:relative}.nav-item{gap:var(--nav-item-gap)}.nav-item>a{align-items:center;background-color:var(--nav-item-bg);color:var(--nav-item-color);cursor:pointer;display:flex;gap:var(--nav-gap);height:var(--nav-item-height);justify-content:center;padding-left:var(--nav-item-padding);padding-right:var(--nav-item-padding)}.nav-item>a>.text{white-space:nowrap}.nav-item>.active{font-weight:700;--nav-item-color:var(--nav-active-color);--nav-item-bg:var(--nav-active-bg)}.nav-item>.selected{--nav-item-color:var(--nav-selected-color);--nav-item-bg:var(--nav-selected-bg)}.nav-item>.disabled{--nav-item-color:var(--nav-disabled-color);--nav-item-bg:var(--nav-disabled-bg)}.nav-item>.disabled>*{opacity:var(--opacity-disabled)}.nav-divider,.nav>.divider{align-self:center;height:var(--nav-divider-height);margin-left:var(--nav-divider-margin);margin-right:var(--nav-divider-margin)}.nav-heading{align-items:center;color:var(--nav-heading-color);display:flex;font-weight:700;gap:.25rem;height:2rem;justify-content:center;padding-left:1rem;padding-right:1rem}.nav-space{flex:1 1 auto;width:1rem}.nav-compact{--nav-gap:0.25rem;--nav-item-gap:0.5rem;--nav-item-height:1.75rem;--nav-item-padding:0.5rem;--nav-divider-margin:0.25rem}.nav-primary>.nav-item{position:relative}.nav-primary>.nav-item:hover{z-index:10}.nav-primary>.nav-item+.nav-item{margin-left:-1px}.nav-primary>.nav-item>a{border-radius:var(--radius-none);border-width:1px}.nav-primary>.nav-item:first-child>a,.nav-primary>.nav-item:has(.nav-heading)+.nav-item>a{border-bottom-left-radius:var(--nav-radius);border-top-left-radius:var(--nav-radius)}.nav-primary>.nav-item:last-child>a{border-bottom-right-radius:var(--nav-radius);border-top-right-radius:var(--nav-radius)}.nav-primary>.nav-item>.active{--tw-text-opacity:1;background:var(--nav-active-color);border-color:var(--nav-active-color);color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.nav-primary>.nav-divider{display:none}.nav-secondary>.nav-item>a{border-radius:var(--radius-none);position:relative}.nav-secondary>.nav-item>a:after{bottom:-2px;display:block;height:2px;left:0;position:absolute;right:0;--tw-bg-opacity:1;background-color:rgba(var(--color-border-rgb),var(--tw-bg-opacity));--tw-content:"";content:var(--tw-content)}.nav-secondary>.nav-item>.active:after{background-color:currentColor}.nav-secondary>.nav-divider{margin:0 0 0 -1px}.nav-tabs>.nav-item>a{border-bottom-left-radius:var(--radius-none);border-bottom-right-radius:var(--radius-none);position:relative}.nav-tabs>.nav-item>a:after{border-color:transparent;border-width:1px;bottom:-1px;display:block;left:-1px;pointer-events:none;position:absolute;right:-1px;top:-1px;--tw-border-opacity:1;border-bottom-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));--tw-content:"";border-top-left-radius:inherit;border-top-right-radius:inherit;content:var(--tw-content)}.nav-tabs>.nav-item>.active:after{--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));border-bottom-color:transparent}.nav-tabs>.nav-divider{margin:0 0 0 -1px}.nav-pills>.nav-item>a{border-radius:var(--radius-full)}.nav-pills>.nav-item>.active{--tw-text-opacity:1;background:var(--nav-active-color);border-color:var(--nav-active-color);color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.nav-steps{--nav-item-bg:var(--color-canvas);--nav-active-bg:var(--color-primary-500);--nav-active-color:var(--color-canvas);--nav-selected-bg:var(--color-secondary-500);--nav-selected-color:var(--color-canvas)}.nav-steps>.nav-item+.nav-item>a{padding-left:calc(var(--nav-item-padding) + var(--nav-item-height)/3)}.nav-steps>.nav-item>a:after,.nav-steps>.nav-item>a:before{border-style:solid;height:0;left:100%;position:absolute;width:0;z-index:2;--tw-content:"";border-color:transparent;border-left-color:var(--nav-item-bg,var(--color-canvas));border-width:calc(var(--nav-item-height)/2);border-left-width:calc(var(--nav-item-height)/3);border-right-width:0;content:var(--tw-content)}.nav-steps>.nav-item>a:before{background-color:transparent!important;border-left-color:var(--color-border);bottom:auto;left:calc(100% + 5.5px);opacity:1!important;right:auto;top:.4rem;transform:none;z-index:1;--nav-item-height:1.2rem}.nav-steps>.nav-item:has(+.nav-item>.active)>.selected:before,.nav-steps>.nav-item:has(+.nav-item>.active,+.nav-item>.selected)>a:not(.active,.selected):before,.nav-steps>.nav-item:has(+.nav-item>.selected)>.active:before,.nav-steps>.nav-item:last-child>a:after,.nav-steps>.nav-item:last-child>a:before{display:none}.nav-steps>.nav-item>a:hover{--nav-item-bg:var(--color-gray-100)}.nav-steps>.nav-item>.active:hover{--nav-item-bg:var(--color-primary-600)}.nav-steps>.nav-item>.selected:hover{--nav-item-bg:var(--color-secondary-600)}.nav-stacked{flex-direction:column}.nav-stacked>.nav-item{width:100%}.nav-stacked>.nav-heading,.nav-stacked>.nav-item>a{justify-content:flex-start;width:100%}.nav-stacked>.nav-divider{height:1px;margin:.5rem 0;width:100%}.nav-primary.nav-stacked>.nav-item>a{height:2.5rem}.nav-primary.nav-stacked>.nav-item+.nav-item{margin-left:0;margin-top:-1px}.nav-primary.nav-stacked>.nav-item:first-child>a{border-bottom-left-radius:0;border-top-left-radius:var(--nav-radius);border-top-right-radius:var(--nav-radius)}.nav-primary.nav-stacked>.nav-item:last-child>a{border-bottom-left-radius:var(--nav-radius);border-bottom-right-radius:var(--nav-radius);border-top-right-radius:0}.nav-secondary.nav-stacked>.nav-item>a:after{bottom:0;height:100%;left:auto;right:0;top:0;width:2px;--tw-bg-opacity:1;background-color:rgba(var(--color-border-rgb),var(--tw-bg-opacity))}.nav-secondary.nav-stacked>.nav-item>.active:after{background-color:currentColor}.nav-secondary.nav-stacked>.nav-divider{margin:0}.nav-tabs.nav-stacked>.nav-item>a{border-bottom-left-radius:var(--nav-radius);border-bottom-right-radius:var(--radius-none);border-top-right-radius:var(--radius-none);position:relative}.nav-tabs.nav-stacked>.nav-item>a:after{--tw-border-opacity:1;border-bottom-color:transparent;border-bottom-left-radius:inherit;border-right-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));border-top-left-radius:inherit}.nav-tabs.nav-stacked>.nav-item>a.active:after{--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));border-right-color:transparent}.nav-tabs.nav-stacked>.nav-divider{margin:0}.nav-pills.nav-stacked>.nav-item+.nav-item{margin-top:.25rem}.nav-justified>.nav-item:not(.flex-none,.nav-divider){flex:1 1 auto}.nav-justified>.nav-item>a{width:100%}.arrow,.arrow:before{background:inherit;height:calc(var(--arrow-size)*1.4142);position:absolute;width:calc(var(--arrow-size)*1.4142)}.arrow{visibility:hidden;--arrow-size:5px}.arrow:before{border:inherit;content:"";visibility:visible;--tw-rotate:45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.arrow-top,.arrow-up{border-bottom:inherit;border-right:inherit;border-width:1px 0 0 1px;top:calc(1px - var(--arrow-size))}.white>.arrow-top,.white>.arrow-up{top:calc(0px - var(--arrow-size))}.arrow-bottom,.arrow-down{border-left:inherit;border-top:inherit;border-width:0 1px 1px 0;bottom:calc(1px - var(--arrow-size))}.white>.arrow-bottom,.white>.arrow-down{bottom:calc(0px - var(--arrow-size))}.arrow-left{border-right:inherit;border-top:inherit;border-width:0 0 1px 1px;left:calc(1px - var(--arrow-size))}.white>.arrow-left{left:calc(0px - var(--arrow-size))}.arrow-right{border-bottom:inherit;border-left:inherit;border-width:1px 1px 0 0;right:calc(1px - var(--arrow-size))}.white>.arrow-right{right:calc(0px - var(--arrow-size))}.popover{display:none;position:absolute;width:-moz-max-content;width:max-content}.popover.show{display:block}.popover-heading{align-items:center;border-top-left-radius:inherit;border-top-right-radius:inherit;display:flex;gap:1rem;height:2.5rem;justify-content:space-between;position:relative;z-index:1;--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity));padding-left:.75rem;padding-right:.5rem}.popover-title{flex:1 1 auto;font-weight:700}.popover-content{padding:.75rem;position:relative;z-index:1}.popover-arrow{z-index:0}.has-heading>.popover-arrow.arrow-top,.has-heading>.popover-arrow.arrow-up{--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity))}.popover>.btn-close{position:absolute;right:.5rem;top:.625rem;z-index:2}.btn-close+.popover-content{padding-right:3rem}.btn.with-popover-show{--tw-shadow:inset 0 2px 4px 0 rgba(0,0,0,.05);--tw-shadow-colored:inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.with-popover-show[data-pop-placement=top]>.caret{--tw-rotate:180deg}.with-popover-show[data-pop-placement=left]>.caret,.with-popover-show[data-pop-placement=top]>.caret{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.with-popover-show[data-pop-placement=left]>.caret{--tw-rotate:90deg}.with-popover-show[data-pop-placement=right]>.caret{--tw-rotate:-90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.pager{align-items:center;display:flex;gap:.25rem;white-space:nowrap}.menu.pager-size-menu{display:grid!important;grid-template-columns:repeat(3,minmax(0,1fr));text-align:center}.menu.pager-size-menu>.menu-item>a{margin:.125rem}.pager .pager-goto-group>.form-control{width:3rem}.pager .pager-goto-group>.input-group-addon{border-width:1px;--tw-border-opacity:1;border-color:rgba(var(--color-gray-300-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.pager>.pager-nav.active{--tw-text-opacity:1;box-shadow:0 0 0 1px var(--color-primary-500);color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.panel{border-radius:var(--radius);overflow:hidden;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.05}.panel-heading{background-color:var(--panel-heading-bg);flex-wrap:nowrap;gap:1rem;justify-content:space-between;padding:.5rem 1rem}.panel-heading,.panel-title{align-items:center;display:flex}.panel-title{font-weight:700;gap:.5rem;margin:0}.panel-actions{align-items:center;display:flex;margin-right:-.5rem}.panel-body{padding:.75rem 1rem}.panel-footer{align-items:center;background-color:var(--panel-footer-bg);display:flex;gap:.5rem;padding:.5rem 1rem}.size-sm .panel-body,.size-sm .panel-footer,.size-sm .panel-heading{padding:.375rem .75rem}.size-lg .panel-body,.size-lg .panel-footer,.size-lg .panel-heading{padding:1rem 1.25rem}.picker{position:relative}.picker-select{align-items:center;gap:.25rem;justify-content:space-between}.picker-select,.picker-select.form-control{display:flex}.picker-select-placeholder{flex:1 1 auto;--tw-text-opacity:1;color:rgba(var(--color-placeholder-rgb),var(--tw-text-opacity))}.picker-select-multi .picker-select-placeholder{padding-left:.25rem;padding-right:.25rem}.picker-single-selection{flex:1 1 auto;min-width:0;overflow:hidden;white-space:nowrap}.picker-deselect-btn{border-radius:var(--radius-full);padding:.25rem}.picker-deselect-btn:not(:hover){opacity:.7}.picker-select-multi.form-control{height:auto;min-height:32px;padding-left:.25rem}.picker-multi-selections{display:flex;flex-wrap:wrap;gap:.25rem;min-width:0}.picker-multi-selection{align-items:center;border-radius:var(--radius-full);border-width:1px;display:flex;max-width:100%;--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity));padding-left:.5rem}.readonly .picker-multi-selection{padding-right:.5rem}.picker-multi-selection>.text{max-width:180px;min-width:0;overflow:clip;white-space:nowrap}.picker-no-search{cursor:default}.picker-menu-list{border-style:none;flex:1 1 auto;min-height:0;overflow-y:auto;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.picker-menu-search+.picker-menu-list.menu{margin-top:-.125rem;padding-top:0}.picker-menu-list.tree{padding:.5rem}.picker-menu-list .tree-item>.selected .item-content{font-weight:400}.picker-menu .search-menu-footer{border-top-width:1px;padding-bottom:.25rem;padding-top:.25rem}.picker-search-input{border-color:transparent;height:1.75rem;outline-width:0!important;padding-left:0;padding-right:2rem;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important;--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)!important;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)!important}.picker-search{flex:none;position:relative;width:100%}.picker-search>.magnifier{opacity:.5;position:absolute;right:0;top:.5rem}.picker-search-clear.btn{cursor:pointer;height:1.25rem;position:absolute;right:0;top:.25rem;width:1.25rem}.picker-search.is-inline{min-width:64px;width:4rem}.is-inline>.picker-search-input{height:1.25rem;min-width:64px;padding-left:.25rem;padding-right:.25rem}.picker-search-measure{height:0;left:0;overflow:hidden;position:absolute;top:0;white-space:nowrap}.split{display:flex;flex-direction:row}.split-vert{flex-direction:column}.split-cell{min-height:0;min-width:0}.has-animation>.split-cell{transition-duration:.15s;transition-property:width,height;transition-timing-function:cubic-bezier(.4,0,.2,1)}.gutter{align-items:stretch;border-radius:var(--radius);cursor:col-resize;display:flex;flex:none;flex-direction:row;justify-content:center;position:relative}.gutter-vert{cursor:row-resize;flex-direction:column}.gutter:before{display:block;--tw-bg-opacity:1;background-color:rgba(var(--color-border-rgb),var(--tw-bg-opacity));transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);--tw-content:"";content:var(--tw-content)}.gutter-horz:before{width:1px}.gutter-vert:before{height:1px}.gutter-horz:hover:before{width:75%}.gutter-vert:hover:before{height:75%}.gutter-resize-handler,.gutter-toggle{align-items:center;border-radius:var(--radius-lg);border-width:1px;display:flex;height:1.75rem;justify-content:center;margin-top:-.875rem;position:absolute;top:50%;width:.75rem;--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-fore-rgb),var(--tw-text-opacity));transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.gutter-toggle>span{--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.gutter:hover .gutter-toggle{--tw-border-opacity:1;border-color:rgba(var(--color-gray-500-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-gray-100-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.gutter .gutter-toggle:hover{--tw-border-opacity:1;border-color:rgba(var(--color-primary-500-rgb),var(--tw-border-opacity));--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.is-last>.gutter-toggle{--tw-scale-x:-1}.is-first>.gutter-toggle,.is-last>.gutter-toggle{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.is-first>.gutter-toggle{--tw-scale-x:1}.gutter-vert .gutter-toggle{left:50%;margin-left:-.375rem;margin-top:0;top:auto;--tw-rotate:90deg}.gutter-vert .gutter-toggle,.gutter.is-next-collapsed>.gutter-toggle{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.gutter.is-next-collapsed>.gutter-toggle{--tw-scale-x:1}.gutter.is-prev-collapsed>.gutter-toggle{--tw-scale-x:-1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.gutter-horz.is-first.is-prev-collapsed{transform:translateX(calc(-1px - var(--split-gutter-size)/2))}.gutter-horz.is-last.is-next-collapsed{transform:translateX(calc(1px + var(--split-gutter-size)/2))}.gutter-vert.is-first.is-prev-collapsed{transform:translateY(calc(-1px - var(--split-gutter-size)/2))}.gutter-vert.is-last.is-next-collapsed{transform:translateY(calc(1px + var(--split-gutter-size)/2))}.gutter-resize-handler{background-color:transparent;border-width:0}.gutter-resize-handler:before{border-left-width:1px;border-right-width:1px;bottom:0;left:.25rem;position:absolute;right:.25rem;top:0;--tw-content:"";content:var(--tw-content)}.sidebar{flex:none!important;position:relative;--gutter-width:1px;--sidebar-duration:200ms}.has-sidebar-animation .sidebar{transition-duration:.15s;transition-duration:var(--sidebar-duration);transition-property:width,margin;transition-timing-function:cubic-bezier(.4,0,.2,1)}.sidebar-left.is-collapsed{margin-left:calc(var(--gutter-width)*-1)}.sidebar-right.is-collapsed{margin-right:calc(var(--gutter-width)*-1)}.sidebar.is-collapsed>*,.sidebar.is-expanded.is-animating>*{display:none}.sidebar.is-collapsed>.sidebar-gutter{display:flex!important}.sidebar-gutter{bottom:0;left:100%;position:absolute;top:0;width:var(--gutter-width)}.sidebar-right>.sidebar-gutter{left:auto;right:100%}.is-collapsed>.gutter>.gutter-toggle>span{--tw-rotate:-180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.sidebar-gutter:before{border-radius:var(--radius-full);opacity:0}.sidebar-gutter:hover:before{opacity:1;width:.5rem}.is-expanded .sidebar-gutter.gutter-border:before{opacity:1}.is-sidebar-resizing{cursor:col-resize}:root{--table-head-bg:var(--color-surface);--table-striped-color:var(--color-gray-50);--table-hover-color:rgba(var(--color-primary-500-rgb),.05);--table-border-color:var(--color-border)}.table{border-color:var(--table-border-color);width:100%}.table>thead{background-color:var(--table-head-bg)}.table>*>tr{border-bottom-width:1px}.table>*>tr>*{padding:.5rem 1rem}.table-striped>tbody>tr:nth-child(2n){background:var(--table-striped-color)}.table-hover>tbody>tr:hover>*{background:var(--table-hover-color)}.table.bordered>*>tr>*{border-width:1px}.table.borderless>*>tr,.table.borderless>*>tr>*{border-style:none}.condensed>*>tr>*{padding:.375rem .75rem}.table-fixed{table-layout:fixed}.table-fixed>*>tr>*{overflow:hidden;white-space:nowrap}.toolbar{align-items:center;display:flex;position:relative}.toolbar>.divider{margin:.5rem;width:1px;--tw-bg-opacity:1;background-color:rgba(var(--color-border-rgb),var(--tw-bg-opacity))}.toolbar-space{flex:1 1 auto;height:1px;width:1px}.toolbar>.dropdown.show{align-items:center;display:flex;position:relative}:root{--tooltip-bg:var(--skin-color,var(--color-inverse));--tooltip-text:var(--skin-text,var(--color-canvas));--tooltip-arrow:6px;--tooltip-delay:0.5s}.hint,[class*=" hint-"],[class^=hint-]{position:relative}.hint:before,[class*=" hint-"]:before,[class^=hint-]:before{border-style:solid;height:0;position:absolute;width:0;--tw-content:"";border-color:var(--tooltip-bg) transparent transparent transparent;border-width:var(--tooltip-arrow) var(--tooltip-arrow) 0 var(--tooltip-arrow);content:var(--tw-content);margin-bottom:calc(0px - var(--tooltip-arrow))}.hint:after,[class*=" hint-"]:after,[class^=hint-]:after{background-color:var(--tooltip-bg);border-color:var(--tooltip-color);border-radius:var(--radius);color:var(--tooltip-text);font-size:.75rem;line-height:1rem;max-width:28rem;overflow:hidden;padding:.25rem .5rem;position:absolute;white-space:nowrap;--tw-shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 3px 8px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-content:attr(data-hint);content:var(--tw-content);text-shadow:0 1px 0 var(--tooltip-bg)}.hint:after,.hint:before,[class*=" hint-"]:after,[class*=" hint-"]:before,[class^=hint-]:after,[class^=hint-]:before{bottom:100%;left:50%;opacity:0;pointer-events:none;transition-delay:0s;transition-duration:.15s;transition-property:opacity,visibility;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,1,1);visibility:hidden;z-index:1800;--tw-translate-x:-50%;--tw-translate-y:calc(var(--tooltip-arrow)*-1);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hint:hover:after,.hint:hover:before,[class*=" hint-"]:hover:after,[class*=" hint-"]:hover:before,[class^=hint-]:hover:after,[class^=hint-]:hover:before{opacity:1;transition-delay:var(--tooltip-delay);visibility:visible}.hint-bottom:before{border-color:transparent transparent var(--tooltip-bg) transparent;border-width:0 var(--tooltip-arrow) var(--tooltip-arrow) var(--tooltip-arrow);margin-bottom:auto;margin-top:calc(0px - var(--tooltip-arrow))}.hint-bottom:after,.hint-bottom:before{bottom:auto;top:100%;--tw-translate-y:var(--tooltip-arrow);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hint-left:before{border-color:transparent transparent transparent var(--tooltip-bg);border-width:var(--tooltip-arrow) 0 var(--tooltip-arrow) var(--tooltip-arrow);margin-bottom:auto;margin-right:calc(0px - var(--tooltip-arrow))}.hint-left:after,.hint-left:before{bottom:50%;left:auto;right:100%;--tw-translate-x:calc(var(--tooltip-arrow)*-1);--tw-translate-y:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hint-right:before{border-color:transparent var(--tooltip-bg) transparent;border-width:var(--tooltip-arrow) var(--tooltip-arrow) var(--tooltip-arrow) 0;margin-bottom:auto;margin-left:calc(0px - var(--tooltip-arrow))}.hint-right:after,.hint-right:before{bottom:50%;left:100%;--tw-translate-x:var(--tooltip-arrow);--tw-translate-y:50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hint-lg:after,.hint-sm .hint-md:after:after,.hint-xl:after{white-space:normal;word-break:break-all}.hint-sm:after{width:4rem}.hint-md:after{width:8rem}.hint-lg:after{width:16rem}.hint-xl:after{width:24rem}.hint-primary{--tooltip-bg:var(--color-primary-500)}.hint-success{--tooltip-bg:var(--color-success-500)}.hint-danger{--tooltip-bg:var(--color-danger-500)}.hint-warning{--tooltip-bg:var(--color-warning-500)}.hint-important{--tooltip-bg:var(--color-important-500)}.hint-special{--tooltip-bg:var(--color-special-500)}.tooltip{background-color:var(--tooltip-bg);border-color:var(--tooltip-color);border-radius:var(--radius);color:var(--tooltip-text);font-size:.75rem;line-height:1rem;max-width:28rem;padding:.25rem .5rem;z-index:1800}.tooltip:not(.white)>.arrow{border-width:0}.tooltip.white>.arrow{border-color:inherit}.tooltip-has-title{--tw-bg-opacity:0.8;padding:.5rem .75rem .75rem;--tw-shadow:0 10px 25px -5px rgba(0,0,0,.2),0 4px 10px -6px rgba(0,0,0,.15);--tw-shadow-colored:0 10px 25px -5px var(--tw-shadow-color),0 4px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.tooltip-has-title>.arrow{opacity:.75}.tooltip-heading{align-items:center;display:flex;font-size:.8125rem;font-weight:700;gap:1rem;justify-content:space-between;line-height:1.25rem;padding:.25rem}.tooltip-heading>.btn-close{margin-right:-.25rem}.tooltip-content{padding:.25rem}.tree-item-inner{flex-direction:row;min-height:24px;padding-left:var(--list-nested-indent)}.tree-item-inner,.tree-toggle{align-items:center;display:flex}.tree-toggle{border-radius:var(--radius);flex:none;height:1rem;justify-content:center;margin-right:-.375rem;width:1rem;--caret-size:5px;--caret-opacity:.3}.tree-item-inner>.item-icon{opacity:.5;width:1rem}.tree-item-inner:hover>.item-icon{opacity:.8}.tree-item-inner.has-hover{border-radius:var(--radius)}.tree-item-inner .item-title:not(:hover),.tree-item-inner>a.item-title:not(:hover){color:inherit}.tree-item-inner>.toolbar{margin-left:1rem;opacity:.6}.tree-item-inner:hover>.toolbar{opacity:1}.tree-item>.active{background:var(--menu-active-bg);color:var(--menu-active-color)}.tree-item>.selected{background:var(--menu-selected-bg)}.tree-item>.selected .item-content{color:var(--menu-selected-color);font-weight:700}.tree.search-menu-on-top>.search-box{left:0;right:0}.tree-lines .tree{position:relative}.tree-lines .tree-item{--tree-line-indent:calc(var(--list-nested-indent) + 8px)}.tree-lines .tree:before{border-left-width:1px;bottom:.5rem;display:block;left:var(--tree-line-indent);position:absolute;top:-.5rem;--tw-content:"";content:var(--tw-content)}.upload .upload-tip{--tw-text-opacity:1;color:rgba(var(--color-gray-400-rgb),var(--tw-text-opacity))}.upload .draggable-area{background-color:rgba(var(--color-primary-50-rgb),.2);border:1px dashed var(--color-primary-300)}.upload .draggable-area.dragover{background-color:rgba(var(--color-primary-500-rgb),.1);border-style:solid}.upload .upload-tip{color:var(--color-slate-500)}.file-item>.img{height:120px;width:120px}.upload .file-item:hover .file-size{display:flex!important}.form-batch-container{overflow-y:auto}.form-batch-table{min-width:100%;table-layout:fixed;--table-head-bg:var(--color-canvas)}.form-batch-table>*>tr{--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.form-batch-table>*>tr>*{padding-left:.375rem;padding-right:.375rem;vertical-align:top}.form-batch-table>*>tr>th{text-align:left;vertical-align:middle}.form-batch-head[data-name=ACTIONS]{width:5rem}.form-batch-head[data-name=ACTIONS][data-action-count="3"]{width:6rem}.form-batch-label{display:inline}.form-batch-control>textarea.form-control{height:2rem}.form-batch-btn+.btn{margin-left:.25rem}.form-batch-table>tbody[data-count="1"] .form-batch-btn[data-type=delete]{display:none}.form-batch-ditto{position:relative}.form-batch-ditto-btn{bottom:0;height:auto;opacity:.5;padding-left:.5rem;padding-right:.5rem;position:absolute!important;right:0;top:0}.form-batch-ditto-btn:after{bottom:.5rem;left:-1px;position:absolute;top:.5rem;width:1px;--tw-bg-opacity:1;background-color:rgba(var(--color-border-rgb),var(--tw-bg-opacity));--tw-content:"";content:var(--tw-content)}[data-ditto=on] .form-batch-ditto-btn{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity));opacity:1}.form-batch-row-actions{padding-bottom:.75rem!important;padding-top:.75rem!important}.form-batch-ditto-wrapper{position:relative}.form-batch-ditto-control .form-control{padding-right:calc(var(--form-batch-ditto-width) + 8px)}.form-batch-ditto-control .input-control>.input-control-suffix{right:var(--form-batch-ditto-width)}.form-batch-ditto-control .input-control>.form-control{padding-right:calc(var(--form-batch-ditto-width) + var(--input-control-suffix))}.form-batch-ditto-control .pick-pri{padding-right:calc(var(--form-batch-ditto-width) + 8px)}.dropmenu-btn{gap:.25rem;padding-left:.5rem;padding-right:.5rem}.dropmenu-btn>.text{max-width:180px;overflow:hidden}.dropmenu-btn>.is-leading{left:-.625rem;opacity:.6;position:absolute}.dropmenu-btn>.is-caret{align-items:center;background-color:rgba(var(--color-canvas-rgb),.2);border-radius:var(--radius);display:flex;height:1rem;justify-content:center;width:1rem}.dropmenu-nav{border-bottom-width:1px;margin-left:.75rem;margin-right:.75rem;margin-top:.25rem}.has-search .dropmenu-nav{margin-top:-.5rem}.dropmenu-nav>.nav-secondary{gap:.5rem}.dropmenu-nav>.nav-secondary>.nav-item>a{border-radius:var(--radius);padding-left:.5rem;padding-right:.5rem}.dropmenu-nav>.nav-secondary>.nav-item>a:after{bottom:-1px;left:1rem;opacity:0;right:1rem}.dropmenu-nav>.nav-secondary>.nav-item>.active:after{left:.5rem;opacity:1;right:.5rem;transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dropmenu-title{font-size:.75rem;font-weight:700;line-height:1rem;margin-bottom:-.5rem;padding:.75rem;--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.dropmenu-list{min-height:0;overflow-x:hidden;overflow-y:auto;padding:.5rem .75rem}.dropmenu.loading>*{opacity:0}.dropmenu-item .tree-item-inner{border-radius:var(--radius);cursor:pointer;transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dropmenu-item.is-item.active>.tree-item-inner{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.tree-item-inner.is-link:hover{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.tree-item-inner.is-link:hover .item-content,.tree-item-inner.is-link:hover .item-trailing-icon,.tree-item-inner.is-link:hover .label{color:inherit}.tree-item-inner.is-toggle:hover{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-100-rgb),var(--tw-bg-opacity))}.dropmenu-item+.tree-item{margin-top:.125rem}.dropmenu-item .is-match{font-weight:700;text-decoration-line:underline}.dropmenu-item .item-title,.dropmenu-item .label{overflow:hidden;white-space:nowrap}.dropmenu-item .item-content{padding-left:.375rem;padding-right:.375rem}.dropmenu-item *+.item-content{padding-left:0}.tree-item-inner.is-toggle .item-title{opacity:.6}.tree-item-inner.is-toggle:hover .item-title{opacity:1}.dropmenu-item-check{align-items:center;display:flex;flex:1 1 auto;justify-content:flex-end;padding-right:.5rem}.dropmenu-item .nested-toggle-icon.is-empty{width:.25rem}.dropmenu-item .item-icon{margin-right:-.25rem}:root{--form-control-border:var(--color-border-strong)}.checkbox-primary.disabled,.checkbox-primary.disabled>label,.checkbox-primary>input[type=checkbox]:disabled,.checkbox-primary>input[type=checkbox]:disabled+label,.radio-primary.disabled,.radio-primary.disabled>label,.radio-primary>input[type=radio],.radio-primary>input[type=radio]:disabled+label{filter:none!important}.checkbox-primary.disabled>label,.radio-primary.disabled>label{opacity:1}.form-actions{gap:1rem;justify-content:center}.form-actions .btn{min-width:96px}.form-control-static{min-height:32px;padding-bottom:.375rem;padding-top:.375rem}:root{--zt-panel-form-max-width:1000px}.size-sm{--zt-panel-form-max-width:800px}.size-lg{--zt-panel-form-max-width:1200px}.panel-body>.container,.panel-heading>.container{max-width:var(--panel-container-width);min-width:0;padding-left:var(--panel-container-padding);padding-right:var(--panel-container-padding)}.panel-heading>.container{align-items:center;display:flex;flex-direction:row;flex-wrap:nowrap;gap:1rem;justify-content:space-between;min-height:48px}.panel-form{border-radius:var(--radius-md);margin-bottom:1rem;margin-left:auto;margin-right:auto;max-width:var(--zt-panel-form-max-width);min-width:var(--zt-panel-form-min-width);overflow:visible;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));padding-bottom:1rem;padding-left:1rem;padding-right:1rem;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.panel-form .form-actions{background-color:rgba(var(--color-canvas-rgb),.8);bottom:0;padding-bottom:1rem;padding-top:1rem;position:sticky;z-index:3;--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.panel-form .form-grid{margin-left:calc(0px - var(--form-grid-gap-y-half));margin-right:calc(0px - var(--form-grid-gap-y-half));padding-bottom:0}.panel-form-toolbar{padding-left:var(--form-grid-gap-x-half);padding-right:var(--form-grid-gap-x-half);width:100%}.panel-form-toolbar>.toolbar{gap:1rem;padding-bottom:.75rem;padding-top:.75rem}.panel-form-divider{margin-bottom:.75rem;margin-top:1rem;padding-left:var(--form-grid-gap-x-half);padding-right:var(--form-grid-gap-x-half);width:100%}.panel-form-divider:before{background-color:rgba(var(--color-border-rgb),.7);display:block;height:1px;--tw-content:"";content:var(--tw-content)}.is-full .btn-full-form,.is-lite .btn-lite-form{z-index:1;--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity));--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-primary-300-rgb),var(--tw-ring-opacity))}.btn-toggle-fold:after{content:attr(data-expand-text)}.show-fold-items .btn-toggle-fold:after{content:attr(data-collapse-text)}.is-foldable,.is-full .full\:hidden,.is-full .lite\:visible,.is-full .panel-form-divider,.is-full .panel-form-toolbar,.is-lite .full\:visible,.is-lite .lite\:hidden,.no-foldable-items .btn-toggle-fold{display:none}.is-foldable.is-pinned,.is-full .full\:visible,.is-full .is-foldable,.is-full .lite\:hidden,.is-lite .full\:hidden,.is-lite .lite\:visible,.show-fold-items .is-foldable{display:block}.is-full .full\:w-1\/2,.is-lite .lite\:w-1\/2{width:50%}.is-full .full\:w-1\/3,.is-lite .lite\:w-1\/3{width:33.333333%}.is-full .full\:w-2\/3,.is-lite .lite\:w-2\/3{width:66.666667%}.is-full .full\:w-1\/4,.is-lite .lite\:w-1\/4{width:25%}.is-full .full\:w-2\/4,.is-lite .lite\:w-2\/4{width:50%}.is-full .full\:w-3\/4,.is-lite .lite\:w-3\/4{width:75%}.is-full .full\:w-1\/5,.is-lite .lite\:w-1\/5{width:20%}.is-full .full\:w-2\/5,.is-lite .lite\:w-2\/5{width:40%}.is-full .full\:w-3\/5,.is-lite .lite\:w-3\/5{width:60%}.is-full .full\:w-4\/5,.is-lite .lite\:w-4\/5{width:80%}.is-full .full\:w-1\/6,.is-lite .lite\:w-1\/6{width:16.666667%}.is-full .full\:w-2\/6,.is-lite .lite\:w-2\/6{width:33.333333%}.is-full .full\:w-3\/6,.is-lite .lite\:w-3\/6{width:50%}.is-full .full\:w-4\/6,.is-lite .lite\:w-4\/6{width:66.666667%}.is-full .full\:w-5\/6,.is-lite .lite\:w-5\/6{width:83.333333%}.is-full .full\:w-full,.is-lite .lite\:w-full{width:100%}.page-form{--panel-container-width:960px;--panel-container-padding:20px;--zt-panel-form-max-width:1200px;--zt-panel-form-min-width:1040px;padding-left:0;padding-right:0;--tw-shadow:0 1px 2px 0 rgba(0,0,0,.04),0 0 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color),0 0 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.page-form .panel-heading{border-bottom-width:1px;--tw-border-opacity:0.7;padding:0}.page-form .panel-title{font-size:1rem;line-height:1.5rem}@media (max-width:1240px){.page-form{--zt-panel-form-max-width:1100px}}@media (max-width:1110px){.page-form{--zt-panel-form-max-width:1040px}}.modal-body>.page-form{margin:-1.5rem -2rem!important;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.modal-body>.page-form>.panel-heading{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius);position:sticky;top:-1.25rem;z-index:2;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));padding-bottom:.375rem;padding-top:.375rem}.modal-body>.panel-form .form-actions{bottom:-1.5rem!important;padding-bottom:1.5rem;padding-top:1.5rem}.dropdown.popover{display:flex;flex-direction:column;position:absolute;z-index:1200}.dropdown.show{display:flex}.dropdown-menu{display:none;--menu-min-width:7rem;--menu-max-width:20rem}.dropdown-menu.show,.menu-wrapper>.dropdown-menu,.show>.dropdown-menu{display:block}.dropdown>.dropdown-menu,.menu-wrapper .dropdown-menu{position:relative}.popup .popup.search-menu{background-color:var(--menu-bg)}.show>.menu-wrapper{display:flex}.dropdown>.menu-wrapper{height:100%}.dropdown>.menu-wrapper .menu-wrapper{position:absolute}.dropdown>.menu-wrapper.is-tree .menu-wrapper{position:relative}.is-contextmenu .is-nested-show>a:not(:hover){--tw-bg-opacity:1;background-color:rgba(var(--color-primary-100-rgb),var(--tw-bg-opacity))}.is-contextmenu .menu-item.is-nested>.item-inner>.nested-toggle-icon{position:absolute;right:.25rem;top:.375rem}.is-contextmenu .menu-nested .menu-item>.item-inner{padding-left:.5rem;padding-right:1.25rem}.is-contextmenu .menu-nested .menu-item>.item-inner>.item-content{overflow:hidden}.dropdown>.arrow{--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.global-search-panel li:not(.order-first):hover{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.global-search-panel li a{width:100%}.global-search-panel li{cursor:pointer}.history-panel>.panel-heading+.panel-body{margin-top:-.75rem}.history-panel-action{position:relative}.history-panel-action:before{border-left-width:1px;bottom:-.375rem;left:.5rem;position:absolute;top:22px;--tw-border-opacity:1;border-color:rgba(var(--color-border-rgb),var(--tw-border-opacity));--tw-content:"";content:var(--tw-content)}.col-reverse>.history-panel-action:first-child:before,.col>.history-panel-action:last-child:before{display:none}.history-panel-action>.item-inner{padding-left:1.5rem}.history-panel-action>.item-inner>.avatar{left:0;position:absolute;top:.375rem}.img-cutter{background:var(--color-gray-100);border:1px solid #ddd;margin-bottom:20px;padding:10px}.img-cutter .canvas{margin:0 auto;max-width:100%;min-width:50px;position:relative}.img-cutter .cover{bottom:0;left:0;position:absolute;right:0;top:0;z-index:1}.img-cutter .controller{background:none;border:1px dashed hsla(0,0%,100%,.7);cursor:move;position:absolute;z-index:5}.img-cutter .control{background:#000;background:rgba(0,0,0,.6);border:1px solid hsla(0,0%,100%,.6);height:6px;position:absolute;width:6px}.img-cutter .control[data-type=left]{cursor:w-resize;left:-4px;margin-top:-3px;top:50%}.img-cutter .control[data-type=top]{cursor:n-resize;left:50%;margin-left:-3px;top:-4px}.img-cutter .control[data-type=right]{cursor:e-resize;margin-top:-3px;right:-4px;top:50%}.img-cutter .control[data-type=bottom]{bottom:-4px;cursor:s-resize;left:50%;margin-left:-3px}.img-cutter .control[data-type=top-left]{cursor:nw-resize;left:-4px;top:-4px}.img-cutter .control[data-type=top-right]{cursor:ne-resize;right:-4px;top:-4px}.img-cutter .control[data-type=bottom-left]{bottom:-4px;cursor:sw-resize;left:-4px}.img-cutter .control[data-type=bottom-right]{bottom:-4px;cursor:se-resize;right:-4px}.img-cutter .cliper{bottom:0;left:0;position:absolute;right:0;top:0;z-index:2}.messager-lite{border-radius:var(--radius-lg);min-width:160px;--tw-bg-opacity:1!important;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))!important;padding:1rem 1.5rem;--tw-text-opacity:1!important;color:rgba(var(--color-fore-rgb),var(--tw-text-opacity))!important;--skin-ring:transparent!important;--skin-text:inherit!important;--skin-color:var(--color-gray-500)}.messager-lite .alert-icon{color:var(--skin-color);font-size:1.5rem;line-height:2rem}.messager-lite .alert-text{font-size:.875rem;font-weight:700;line-height:1.3rem}.messager-lite .toolbar{gap:.5rem;margin-left:2rem}.messager-lite .alert-content+.alert-close{margin-left:1.5rem}.messager-lite .toolbar .btn.ghost{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.modal-reconfirm .modal-body{font-size:14px;font-weight:700;padding:24px 32px}.modal-reconfirm .modal-footer{justify-content:center;padding:24px}.picker-deselect-btn{opacity:.6;transition-duration:.15s;transition-property:opacity,transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.picker-multi-selection{cursor:pointer;padding-right:.5rem;position:relative}.picker-multi-selection .picker-deselect-btn{bottom:0;left:0;position:absolute;right:0;top:0;--tw-scale-x:0;--tw-scale-y:0;background-color:rgba(var(--color-surface-rgb),.8);opacity:0}.picker-deselect-btn:hover,.picker-multi-selection .picker-deselect-btn,.picker-multi-selection:hover .picker-deselect-btn{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.picker-deselect-btn:hover,.picker-multi-selection:hover .picker-deselect-btn{--tw-scale-x:1;--tw-scale-y:1;opacity:1;--state-color:rgba(var(--color-surface-rgb),.9)}.pick-pri{flex-direction:row;justify-content:flex-start;position:relative}.pick-pri,.pick-pri>.text{align-items:center;display:flex}.pick-pri>.text{flex:1 1 auto;min-width:0;overflow:hidden;white-space:nowrap}.pick-pri .placeholder{--tw-text-opacity:1;color:rgba(var(--color-placeholder-rgb),var(--tw-text-opacity))}.pick-pri-list>.btn{justify-content:flex-start}.disabled.pick-pri,.disabled.pick-pri>*{filter:none}.search-form{--gutter-width:12px;padding-bottom:.5rem}.search-form-container{border-radius:var(--radius);min-height:116px;position:relative;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));--tw-shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 3px 8px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-border-rgb),var(--tw-ring-opacity))}.is-simple .search-form-container{min-height:56px}.search-form-field td{padding:.375rem}.search-form-field td.is-andOr,.search-form-field td.is-operator{width:5rem}.search-form-field td.is-condition{width:7rem}.search-form-field td.is-operator{width:5rem}[lang=en] .search-form-field td.is-operator{width:6rem}.search-form .sidebar-right.is-collapsed{margin-right:-.375rem}.is-simple .search-form-wrapper{display:flex}.is-simple .search-form-wrapper>.row{flex:1 1 auto}.is-simple .search-form-wrapper>.toolbar{flex:none;margin-top:0}.zinbar [data-hint]{position:relative}.zinbar [data-hint]:after{background-color:rgba(var(--color-inverse-rgb),var(--tw-bg-opacity));border-top-left-radius:var(--radius-lg);border-top-right-radius:var(--radius-lg);bottom:100%;display:block;pointer-events:none;position:absolute;right:0;white-space:nowrap;z-index:50;--tw-bg-opacity:0.9;font-size:.75rem;line-height:1rem;padding:.125rem .25rem;--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity));opacity:0;transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);--tw-content:attr(data-hint);content:var(--tw-content)}.zinbar [data-hint]:hover:after{opacity:1}.zin-perf-btn-list .text-danger{color:var(--color-danger-400)}.zinbar .state{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.zinbar .state:active{--tw-scale-x:1.05;--tw-scale-y:1.05}.zinbar .state:active,.zinbar.collapse{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.zinbar.collapse{--tw-translate-x:100%}.zinbar.collapse>div{pointer-events:none}.zinbar{--tw-translate-x:0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));transition-duration:.7s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}body[z-perf=danger]:before,body[z-perf=warning]:before{bottom:0;left:0;pointer-events:none;position:fixed;right:0;top:0;z-index:9990;--tw-border-opacity:1;border-color:rgba(var(--color-warning-600-rgb),var(--tw-border-opacity));font-size:.75rem;line-height:1rem;--tw-content:"";border-style:inset;border-width:12px 8px 8px;content:var(--tw-content)}body[z-perf=danger]:before{--tw-border-opacity:1;border-color:rgba(var(--color-danger-600-rgb),var(--tw-border-opacity))}body[z-perf=danger]:after,body[z-perf=warning]:after{font-size:.75rem;font-weight:700;left:0;line-height:1rem;line-height:1;pointer-events:none;position:fixed;right:0;text-align:center;top:0;z-index:9991;--tw-text-opacity:1;color:rgba(var(--color-white-rgb),var(--tw-text-opacity));--tw-content:attr(z-perf-message);content:var(--tw-content)}.card{border-radius:var(--radius);min-height:28px;position:relative;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));--tw-shadow:0 1px 2px 0 rgba(0,0,0,.04),0 0 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color),0 0 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:rgba(var(--color-border-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.05;transition-duration:.15s;transition-property:box-shadow,border,color,background-color;transition-timing-function:cubic-bezier(.4,0,.2,1)}.card:hover{--tw-shadow:0 2px 6px -1px rgba(0,0,0,.2),0 1px 4px -2px rgba(0,0,0,.15);--tw-shadow-colored:0 2px 6px -1px var(--tw-shadow-color),0 1px 4px -2px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.card-content,.card-footer,.card-header,.card-heading{padding:.25rem .5rem}.card-heading{align-items:center;display:flex;gap:.5rem}.card-icon{opacity:.5}.card-header{--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity))}.card-content{display:flex;flex-direction:column;gap:.25rem}.card-actions{position:absolute;right:.125rem;top:.125rem}.card-actions+.card-heading{padding-right:1.75rem}.card-subtitle{font-size:.75rem;line-height:1rem;--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.card.selected{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity));--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-primary-400-rgb),var(--tw-ring-opacity))}.card-list{--list-gap:.625rem;--list-count-per-row:1}.card-list,.card-list-item{padding:calc(var(--list-gap)/2)}.card-list-item{width:calc(100%/var(--list-count-per-row))}.card-grid{display:flex;flex-wrap:wrap}.card-grid .card{height:100%}.contextmenu{display:none}.contextmenu.show{display:block}.contextmenu .popup .popup,.contextmenu.popup{position:absolute}.more,.more-vert{margin:.5rem;position:relative}.more,.more-vert,.more-vert:after,.more-vert:before,.more:after,.more:before{background-color:currentColor;border-radius:var(--radius-full);height:.125rem;width:.125rem}.more-vert:after,.more-vert:before,.more:after,.more:before{display:block;left:-5px;position:absolute;top:0;--tw-content:"";content:var(--tw-content)}.more-vert:after,.more:after{left:5px}.more-vert{--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dashboard-blocks{position:relative}.dashboard-block-cell{position:absolute;transition-duration:.15s;transition-property:left,top;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dashboard-block{border-radius:var(--radius);bottom:.5rem;left:.5rem;position:absolute;right:.5rem;top:.5rem;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dashboard-block.is-dragging{opacity:0;z-index:1600}.dashboard-block-header{align-items:center;border-top-left-radius:var(--radius);border-top-right-radius:var(--radius);display:flex;flex-direction:row;justify-content:space-between;left:0;padding:.75rem;pointer-events:none;position:absolute;right:0;top:0;z-index:10}.dashboard-block-header>*{pointer-events:auto}.dashboard-block-title{font-size:.875rem;font-weight:700;line-height:1.3rem}.dashboard-block-body{bottom:0;left:0;position:absolute;right:0;top:0}.dashboard-block-body>.panel{display:flex;flex-direction:column;height:100%;width:100%;--tw-shadow:0px 6px 16px rgba(202,215,235,.25)}.dashboard-block-body .panel-heading{cursor:move;flex:none}.dashboard-block-body .panel-body{flex:1 1 auto;overflow-y:auto}.has-more-menu>.dashboard-block-body .panel-heading{padding-right:3rem}.dashboard-block-body .panel-title{font-size:.875rem;height:2rem;line-height:1.3rem}.dashboard-block-actions .btn{opacity:.6}.dashboard-block-actions .btn:hover{opacity:1}.dashboard-drop-shadow{position:absolute;transition-duration:.15s;transition-property:left,top;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dashboard-drop-shadow:before{background-color:rgba(var(--color-inverse-rgb),.05);border-radius:var(--radius);bottom:.5rem;left:.5rem;position:absolute;right:.5rem;top:.5rem;--tw-shadow:inset 0 2px 4px 0 rgba(0,0,0,.05);--tw-shadow-colored:inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-content:"";content:var(--tw-content)}.scrollbar,:root{--scrollbar-size:10px;--scrollbar-opacity:.6;--scrollbar-bg:rgba(var(--color-inverse-rgb),.15);--scrollbar-inset:inset 0 0 0 1px rgba(var(--color-inverse-rgb),.05);--scrollbar-bar-bg:rgba(var(--color-inverse-rgb),.3);--scrollbar-hover-bg:rgba(var(--color-inverse-rgb),.4);--scrollbar-drag-bg:rgba(var(--color-inverse-rgb),.5);--scrollbar-radius:var(--radius-sm);--scrollbar-duration:.7s}.scrollbar{background:var(--scrollbar-bg);box-shadow:var(--scrollbar-shadow);opacity:var(--scrollbar-opacity);position:absolute;transition-duration:.15s;transition-duration:var(--scrollbar-duration);transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.scrollbar-hover .scrollbar{opacity:0}.scrollbar-hover:hover .scrollbar{opacity:var(--scrollbar-opacity)}.scrollbar.is-dragging,.scrollbar:hover{opacity:1!important}.scrollbar-bar{background:var(--scrollbar-bar-bg);border-radius:var(--scrollbar-radius);position:absolute}.scrollbar-bar:hover{background:var(--scrollbar-hover-bg)}.is-dragging>.scrollbar-bar{background:var(--scrollbar-drag-bg)}.is-horz>.scrollbar-bar{height:100%;z-index:20}.is-vert>.scrollbar-bar{width:100%;z-index:20}:root{--dtable-checked-bg:var(--color-warning-50);--dtable-checked-bg-hover:var(--color-warning-100);--dtable-checked-border:var(--color-border-strong)}.dtable-cell.is-checked,.dtable-striped .dtable-cell.is-odd-row.is-checked{background-color:var(--dtable-checked-bg)}.checkbox-primary.dtable-checkbox{margin-right:-.375rem}.dtable-checkbox{order:-5}.dtable-hover-col .dtable-cell.is-hover-col.is-checked,.dtable-hover-row .dtable-cell.is-hover-row.is-checked{background-color:var(--dtable-checked-bg-hover)}.dtable-cell.is-checked{--dtable-border-color:var(--dtable-checked-border)}.dtable-check-info{flex-shrink:1;margin-right:.5rem;overflow:hidden;white-space:nowrap}:root{--toggle-icon-size:calc(var(--font-size-root)*3/4 + 1px)}.toggle-icon,.toggle-icon-collapse,.toggle-icon-expand{border-color:currentColor;border-width:1px;display:block;opacity:.5;position:relative}.toggle-icon-collapse:hover,.toggle-icon-expand:hover,.toggle-icon:hover{opacity:.9}.toggle-icon,.toggle-icon-collapse,.toggle-icon-expand{border-radius:inherit;height:var(--toggle-icon-size);width:var(--toggle-icon-size)}.is-collapsed .toggle-icon:after,.is-collapsed .toggle-icon:before,.is-expanded .toggle-icon:before,.toggle-icon-collapse:after,.toggle-icon-collapse:before,.toggle-icon-expand:before{background-color:currentColor;content:" ";display:block;height:1px;left:2px;position:absolute;right:2px;top:calc((var(--toggle-icon-size) - 3px)/2)}.is-collapsed .toggle-icon:after,.toggle-icon-collapse:after{--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:root{--dtable-nt-size:calc(var(--font-size-root)*3/4 + 1px)}.dtable-nested-indent{flex:none;margin-right:-.25rem;order:-100}.dtable-nested-toggle{align-items:center;border-radius:var(--radius-sm);display:flex;flex:none;height:1.25rem;justify-content:center;margin-left:-.25rem;order:-6;width:1.25rem}.dtable-nested-toggle.is-no-child{margin-right:-.25rem;opacity:0;pointer-events:none}.dtable-child-label{order:-5}.dtable-header-group{align-items:center;background:var(--dtable-header-bg);cursor:default;display:flex;justify-content:center;left:0;position:absolute;top:1px;z-index:30}.dtable-bordered .dtable-header-group{border-bottom:1px solid var(--dtable-border-color);border-left:1px solid var(--dtable-border-color)}.dtable-header-as-group{contain:style;content-visibility:visible;overflow:visible!important}.dtable-sorting{cursor:move}.dtable-cell.is-sorting-from{background-color:var(--dtable-bg);z-index:20}.dtable-cell.is-sorting-from:before{border-top-width:1px}.dtable-sorting .dtable-cell.is-sorting-after{--tw-translate-y:-100%}.dtable-sorting .dtable-cell.is-sorting-after,.dtable-sorting .dtable-cell.is-sorting-before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dtable-sorting .dtable-cell.is-sorting-before{--tw-translate-y:100%}.dtable-cell.is-sorting-before:before,.dtable-cell.is-sorting-from:before{border-bottom-width:1px;border-top-width:1px}.dtable{--dtable-bg:var(--color-canvas);--dtable-striped-bg:var(--color-gray-50);--dtable-hover-bg:rgba(var(--color-primary-500-rgb),.05);--dtable-header-bg:var(--color-surface);--dtable-footer-bg:var(--color-surface);--dtable-border-color:var(--color-border);--dtable-sorter-size:0.3125rem}.dtable{position:relative;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.dtable.no-animation *{transition-property:none!important}.dtable-block{overflow:hidden;position:absolute;width:100%}.dtable-header{background-color:var(--dtable-header-bg);border-top-left-radius:inherit;border-top-right-radius:inherit}.dtable-body{overflow:hidden}.dtable-cells{background-color:var(--dtable-bg);height:100%;position:absolute;z-index:0}.dtable-cells-container{height:100%;position:absolute;width:100%}.dtable-header .dtable-cells{background-color:var(--dtable-header-bg)}.dtable-cells.dtable-fixed-left,.dtable-cells.dtable-fixed-right{z-index:10}.dtable-cell{position:absolute;transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dtable-cell:after,.dtable-cell:before{bottom:0;content:"";left:0;opacity:0;pointer-events:none;position:absolute;right:0;top:-1px;z-index:2}.dtable-cell:before{border-bottom-width:1px;border-color:var(--dtable-border-color);opacity:1}.dtable-header-cell{align-items:center;display:flex;font-weight:700}.dtable-header-cell:after,.dtable-header-cell:before{top:0}.dtable-cell-content{align-items:center;display:flex;gap:.25rem;height:100%;overflow:hidden;padding-left:.75rem;padding-right:.75rem;position:relative;white-space:nowrap;width:100%;z-index:5}.dtable-body .dtable-cell.is-last-row:before{border-bottom-width:0}.dtable-footer{align-items:center;background-color:var(--dtable-footer-bg);border-bottom-left-radius:inherit;border-bottom-right-radius:inherit;border-color:var(--dtable-border-color);border-top-width:1px;display:flex;padding-left:.75rem;padding-right:.75rem;position:absolute;width:100%;z-index:10}.dtable-empty-tip{align-items:center;display:flex;height:100%;justify-content:center}.dtable .scrollbar{z-index:20}.dtable-header{transition-duration:1s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dtable-scrolled-down .dtable-header{border-bottom-color:rgba(var(--color-inverse-rgb),.15);border-bottom-width:1px}.dtable-has-scroll-y:not(.dtable-scrolled-bottom) .dtable-footer{border-top-color:rgba(var(--color-inverse-rgb),.15);border-top-width:1px}.dtable-scroll-shadow{background-image:linear-gradient(90deg,rgba(0,0,0,.03),transparent),linear-gradient(90deg,rgba(0,0,0,.03),transparent 50%);opacity:0;pointer-events:none;position:absolute;top:0;transition-duration:1s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);width:8px;z-index:20}.dtable:hover .dtable-scroll-shadow{opacity:1}.dtable-scroll-shadow.is-right{margin-left:-7px;--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dtable-scroll-shadow.is-left,.dtable-scrolled-end .dtable-scroll-shadow.is-right{visibility:hidden}.dtable-scroll-shadow.is-right,.dtable-scrolled-right .dtable-scroll-shadow.is-left{visibility:visible}.scrollbar-hover:hover .scrollbar{opacity:0}.scrollbar-hover .dtable-body:hover~.scrollbar{opacity:var(--scrollbar-opacity)}.dtable-sort{display:inline-flex;height:calc(var(--dtable-sorter-size)*12/5);margin-left:.25rem;opacity:.8;position:relative}.dtable-cell:hover .dtable-sort{opacity:1}.dtable-sort:after,.dtable-sort:before{border-color:transparent;border-style:solid;height:0;left:0;opacity:.4;position:absolute;width:0;--tw-content:"";border-width:var(--dtable-sorter-size);content:var(--tw-content)}.dtable-sort:before{border-bottom-color:currentColor;border-top-width:0;top:0}.dtable-sort:after{border-bottom-width:0;border-top-color:currentColor;bottom:0}.dtable-sort.dtable-sort-asc:before,.dtable-sort.dtable-sort-desc:after{opacity:.9}.dtable-sort-link{align-items:center;cursor:pointer;display:inline-flex}.dtable-cells.dtable-fixed-left:before,.dtable-cells.dtable-fixed-right:before{border-color:var(--dtable-border-color);content:"";height:100%;pointer-events:none;position:absolute;width:100%;z-index:5}.dtable-cells.dtable-fixed-left:before{border-right-width:1px;right:-1px}.dtable-cells.dtable-fixed-right:before{border-left-width:1px;left:0}.dtable-bordered:before{border-color:var(--dtable-border-color);border-radius:inherit;border-width:1px;bottom:0;content:"";display:block;left:0;pointer-events:none;position:absolute;right:0;top:0;z-index:15}.dtable-bordered .dtable-cell:before{border-width:1px;right:-1px}.dtable-cell.has-border-left:before{border-left-width:1px}.dtable-cell.has-border-right:before{border-right-width:1px;right:-1px}.dtable-striped .dtable-cell.is-odd-row{background-color:var(--dtable-striped-bg)}.dtable-hover-cell .dtable-cell:hover:after,.dtable-hover-col .dtable-cell.is-hover-col:before,.dtable-hover-row .dtable-body .dtable-cell.is-hover-row:before{background-color:var(--dtable-hover-bg)}.kanban{--kanban-lane-name-width:20px;--kanban-col-width:280px;--kanban-cols-gap:8px;--kanban-lanes-gap:8px;--kanban-header-height:32px;--kanban-lane-height:auto;--kanban-lane-color:var(--color-primary-500);--kanban-link-color:var(--color-secondary-600)}.kanban{align-items:flex-start;display:flex;flex-direction:column;min-width:-moz-fit-content;min-width:fit-content;position:relative}.kanban-header{display:flex;flex-direction:row;height:var(--kanban-header-height)}.kanban.has-sub-cols .kanban-header{height:calc(var(--kanban-header-height)*2)}.kanban-header-lane-name{background-color:rgba(var(--color-fore-rgb),.05);flex:none;width:var(--kanban-lane-name-width)}.kanban-header-cols,.kanban-header-sub-cols{display:flex;flex:1 1 0%;flex-direction:row;gap:var(--kanban-cols-gap)}.kanban-header-sub-cols{border-top-width:2px;--tw-border-opacity:1;border-color:rgba(var(--color-canvas-rgb),var(--tw-border-opacity))}.kanban-header-col{align-items:center;border-bottom-width:1px;display:flex;flex:1 1 0%;flex-direction:column;height:100%;width:var(--kanban-col-width)}.kanban-header-col.has-subs{width:auto}.kanban-header-col-wrapper{align-items:center;display:flex;flex:1 1 0%;flex-direction:row;position:relative;width:100%;--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity));padding-left:3.5rem;padding-right:3.5rem}.kanban-header-col-wrapper.is-align-left{justify-content:flex-start;padding-left:.5rem}.kanban-header-col-wrapper.is-align-left>.kanban-header-title{justify-content:flex-start}.kanban-header-title{align-items:center;display:flex;flex:1 1 auto;flex-direction:row;justify-content:center;overflow:hidden}.kanban-header-title>.as-title{color:var(--kanban-col-color);font-weight:700;overflow:hidden;white-space:nowrap}.kanban-header-col-actions{bottom:.25rem;position:absolute;right:.25rem;top:.25rem}.kanban-header-col-actions>.btn{opacity:.6}.kanban-header-col-actions>.btn:hover{opacity:1}.kanban-body{display:flex;flex-direction:column;gap:var(--kanban-lanes-gap)}.kanban-lane{display:flex;flex-direction:row;height:var(--kanban-lane-height);min-height:96px}.kanban-lane-name{align-items:center;background-color:var(--kanban-lane-color);display:flex;justify-content:center;width:var(--kanban-lane-name-width);--tw-text-opacity:1;color:rgba(var(--color-white-rgb),var(--tw-text-opacity));writing-mode:vertical-rl}.kanban-lane-actions,.kanban-lane-name{flex:none;position:relative}.kanban-lane.is-first .kanban-lane-actions{margin-top:calc(4px - var(--kanban-header-height))}.has-sub-cols .kanban-lane.is-first .kanban-lane-actions{margin-top:calc(4px - var(--kanban-header-height)*2)}.kanban-lane-actions>.btn.square{width:var(--kanban-lane-name-width)}.kanban-lane-title{align-items:center;display:flex;flex:1 1 auto;justify-content:center;overflow:hidden;white-space:nowrap}.kanban-lane-cols{display:flex;flex-direction:row;gap:var(--kanban-cols-gap)}.kanban-lane-col{background-color:rgba(var(--color-inverse-rgb),.05);display:flex;flex-direction:column;height:100%;width:var(--kanban-col-width)}.kanban-header-col,.kanban-lane-col{margin-left:calc(var(--kanban-col-gap-left) - var(--kanban-cols-gap));margin-right:calc(var(--kanban-col-gap-right) - var(--kanban-cols-gap))}.kanban-col-content{position:relative;z-index:5}.kanban-lane.is-first>.kanban-lane-name:before{background-color:var(--kanban-lane-color);bottom:100%;display:block;height:var(--kanban-header-height);left:0;position:absolute;right:0;--tw-content:"";content:var(--tw-content)}.kanban.has-sub-cols .kanban-lane.is-first>.kanban-lane-name:before{height:calc(var(--kanban-header-height)*2)}.kanban-lane{margin-bottom:calc(var(--kanban-col-gap-bottom) - var(--kanban-lanes-gap));margin-top:calc(var(--kanban-col-gap-top) - var(--kanban-lanes-gap))}.kanban-items{flex:1 1 auto;overflow:auto;scrollbar-gutter:both-edges stable}.kanban-lane.is-auto-height .kanban-items{scrollbar-gutter:auto}.kanban-item{cursor:default}.kanban-item .card-actions{z-index:5}.kanban-heading{width:100%}.kanban-heading .listitem{height:2.25rem}.kanban-heading .item-title{font-weight:700}.kanban.has-dragging{cursor:move}.kanban-item.is-dragging{cursor:move;opacity:.1}.kanban-sticky .kanban-header{position:sticky;top:0;z-index:10;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.kanban-sticky .kanban-header.is-pinned{border-bottom-color:rgba(var(--color-inverse-rgb),.15);border-bottom-width:1px;z-index:20;--tw-shadow:0 1px 2px 0 rgba(0,0,0,.04),0 0 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color),0 0 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.kanban-sticky .kanban-lane-name{left:0;position:sticky;z-index:21}.kanban-region{border-radius:var(--radius);min-width:-moz-fit-content;min-width:fit-content;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));padding:.75rem;--tw-shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 3px 8px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.kanban-region>.kanban+.kanban{margin-top:.75rem}.kanban-region.has-heading{padding-top:0}.kanban-heading>.listitem{left:.75rem;position:sticky;width:calc(var(--kanban-list-width) - 24px)}.kanban-list{display:flex;flex-direction:column;gap:.75rem;height:100vh;overflow:auto;position:relative;scrollbar-gutter:stable}.kanban-list.is-moving{cursor:grabbing}.kanban-items.is-dropping{position:relative}.kanban-item.is-dropping{position:relative;transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.kanban-item.is-dropping:after,.kanban-items.is-dropping>.card-list:after{background-color:rgba(var(--color-warning-500-rgb),.5);border-radius:var(--radius-full);display:block;height:.25rem;--tw-content:"";content:var(--tw-content)}.kanban-item.is-dropping:after{left:.25rem;position:absolute;right:.25rem;top:-.25rem}.kanban-item.is-dropping[z-drop-side=before]{--tw-translate-y:0.25rem}.kanban-item.is-dropping[z-drop-side=after],.kanban-item.is-dropping[z-drop-side=before]{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.kanban-item.is-dropping[z-drop-side=after]{--tw-translate-y:-0.25rem}.kanban-item.is-dropping[z-drop-side=after]:after{bottom:-.25rem;top:auto}.kanban-links{bottom:0;left:0;pointer-events:none;position:absolute;right:0;top:0;z-index:5}.kanban-list>.kanban-links{overflow:visible;z-index:11}.kanban-link{align-items:center;color:var(--kanban-link-color);display:flex;justify-content:center;position:absolute}.kanban-link>svg{position:absolute}.kanban-link-text{border-radius:var(--radius);height:1em;line-height:1;padding-left:.125rem;padding-right:.125rem;pointer-events:auto;position:relative;text-align:center;white-space:nowrap}.kanban-link-start-point{height:.5rem;width:.5rem;--tw-translate-x:-0.25rem;--tw-translate-y:-0.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.kanban-link-start-point,.kanban-link-start-point:before{background-color:currentColor;border-radius:var(--radius-full);position:absolute}.kanban-link-start-point:before{bottom:-.25rem;display:block;left:-.25rem;opacity:.2;right:-.25rem;top:-.25rem;--tw-content:"";content:var(--tw-content)}.kanban-link path{pointer-events:auto}.kanban-link path+path{opacity:.6}.kanban-link.is-hover path+path{opacity:1;stroke-width:calc(var(--kanban-link-weight)*1.5)}.kanban-link-delete-btn{pointer-events:auto;--tw-scale-x:.75;--tw-scale-y:.75;opacity:0;transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.is-hover .kanban-link-delete-btn,.kanban-link-delete-btn{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.is-hover .kanban-link-delete-btn{--tw-scale-x:1;--tw-scale-y:1;opacity:1}.is-adding-link{cursor:crosshair;-webkit-user-select:none;-moz-user-select:none;user-select:none}.kanban-link-editor{bottom:0;left:0;pointer-events:none;position:absolute;right:0;top:0;z-index:20}.kanban-link-editor-from,.kanban-link-editor-to{border-radius:var(--radius);--tw-shadow-color:rgb(var(--color-primary-500-rgb));--tw-shadow:var(--tw-shadow-colored);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-primary-500-rgb),var(--tw-ring-opacity))}.kanban-link-editor-from,.kanban-link-editor-point,.kanban-link-editor-to{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);position:absolute}.kanban-link-editor-point{height:.5rem;pointer-events:auto;width:.5rem;--tw-translate-x:-0.25rem;--tw-translate-y:-0.25rem;border-radius:var(--radius-full);cursor:move;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));opacity:.5;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);--tw-ring-color:rgba(var(--color-primary-500-rgb),0.1);transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.kanban-link-editor-point.is-left{left:0;top:50%}.kanban-link-editor-point.is-right{right:0;top:50%;--tw-translate-x:0.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.kanban-link-editor-point.is-top{left:50%;top:0}.kanban-link-editor-point.is-bottom{bottom:0;left:50%;--tw-translate-y:0.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.kanban-link-editor-point:hover{opacity:1;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(8px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:rgba(var(--color-primary-500-rgb),0.5)}.tab-pane{display:none}.tab-pane.active{display:block}.has-sorting-col{cursor:move;--dtable-sorting-col-color:var(--color-primary-300);--dtable-hover-bg:transparent}.has-sorting-col .dtable-col-splitter{display:none}.dtable-cell.is-sorting-col-move{transition-duration:.15s;transition-property:left;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dtable-cell.is-sorting-from-col{transition-duration:.1s}.dtable-cell.is-sorting-from-col:after{background-color:var(--dtable-sorting-col-color)!important;opacity:.1!important}.dtable-cell.is-sorting-from-col:before{border-left:1px solid var(--dtable-sorting-col-color);border-right:1px solid var(--dtable-sorting-col-color)}.dtable-header-cell.is-sortable-col{cursor:move}.has-sorting-col .scrollbar.is-horz{opacity:var(--scrollbar-opacity)!important}.has-sorting-col .dtable-scroll-shadow{visibility:visible}:root{--dtable-col-splitter-width:7px;--dtable-col-splitter-color:var(--color-primary-500)}.dtable-cell.has-col-resizing:after,.dtable-cell.has-col-splitter:hover:after{background:var(--dtable-col-splitter-color);opacity:.05}.dtable-col-splitter{background:var(--dtable-col-splitter-color);border-radius:var(--radius-full);bottom:0;cursor:ew-resize;opacity:0;position:absolute;right:calc(var(--dtable-col-splitter-width)/-2);top:0;transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);width:var(--dtable-col-splitter-width);z-index:20}.dtable-cell.has-col-splitter:hover .dtable-col-splitter{opacity:.3}.dtable-cell.has-col-resizing .dtable-col-splitter,.dtable-cell.has-col-splitter .dtable-col-splitter:hover{opacity:.5}.dtable.has-col-resizing .dtable-cell.has-col-splitter:not(.has-col-resizing) .dtable-col-splitter,.dtable.has-col-resizing .dtable-cell.has-col-splitter:not(.has-col-resizing):after{opacity:0}.dtable-col-splitter:after,.dtable-col-splitter:before{content:" ";display:block;height:.5rem;left:50%;margin-top:-.25rem;position:absolute;top:50%;width:1px;--tw-bg-opacity:1;background-color:rgba(var(--color-white-rgb),var(--tw-bg-opacity))}.dtable-col-splitter:before{margin-left:-1px}.dtable-col-splitter:after{margin-left:1px}.dtable.has-col-resizing{--dtable-hover-bg:transparent}.dtable.has-col-resizing,.has-col-resizing .dtable-header-cell{cursor:ew-resize!important}.dtable-cell.is-col-resizing:before{border-right:1px solid var(--dtable-col-splitter-color)}.dtable-cell.is-col-resizing:after{background:var(--dtable-col-splitter-color);opacity:.05}.has-col-resizing .scrollbar.is-horz{opacity:var(--scrollbar-opacity)!important}.dtable-cell.has-contextmenu-show:after{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));opacity:.1}.dtable{--dtable-header-bg:var(--color-surface-light);--dtable-footer-bg:var(--color-surface-light);--dtable-border-color:var(--color-border-light);--dtable-checked-border:var(--color-border);--dtable-checked-bg:var(--color-primary-50);--dtable-checked-bg-hover:var(--color-primary-100);--dtable-hover-bg:rgba(var(--color-gray-500-rgb),.05);--dtable-link-color:var(--color-link);--dtable-link-hover-color:var(--color-link-hover);--dtable-link-visited-color:var(--color-link-visited)}.dtable-cell-content[style*="--color-link"]{--dtable-link-hover-color:var(--color-link);--dtable-link-visited-color:var(--color-link)}.dtable-body .dtable-cell a{--dtable-link-color:var(--color-link)}.dtable-body .dtable-cell a:not(.btn){color:var(--dtable-link-color)}.dtable-body .dtable-cell a:not(.btn):visited{color:var(--dtable-link-visited-color)}.dtable-body .dtable-cell a:not(.btn):hover{color:var(--dtable-link-hover-color)}.dtable .btn-avatar{border-radius:var(--radius-full);gap:.25rem;height:1.25rem;--tw-bg-opacity:1;background-color:rgba(var(--color-gray-100-rgb),var(--tw-bg-opacity));padding-left:0;padding-right:.5rem;--tw-ring-color:transparent}.dtable .btn-avatar:hover{--tw-shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 3px 8px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.dtable-nested-toggle{width:1rem}.dtable .toggle-icon{--toggle-icon-size:11px;border-radius:var(--radius);--tw-border-opacity:1;border-color:rgba(var(--color-gray-400-rgb),var(--tw-border-opacity))}.dtable .toggle-icon:after,.dtable .toggle-icon:before{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-700-rgb),var(--tw-bg-opacity))}.dtable-footer>.toolbar{margin-right:.5rem}.dtable-header .dtable-cell[data-sort]>.dtable-cell-content>a:not(:hover){color:inherit}.dtable-assign-btn{align-items:center;border-radius:var(--radius-full);display:flex;flex-direction:row;height:1.25rem;padding-left:.375rem;padding-right:.375rem;--dtable-link-color:var(--color-fore)}.dtable-assign-btn:not(:hover){color:inherit}.dtable-assign-btn.is-me{--dtable-link-color:var(--color-warning-500)!important}.dtable-assign-btn:hover{--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-border-strong-rgb),var(--tw-ring-opacity))}.dtable-assign-btn>span{border-radius:var(--radius-full);line-height:20px;padding-left:.375rem;padding-right:.375rem}.dtable-assign-btn.is-unassigned>span{--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity));--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-border-strong-rgb),var(--tw-ring-opacity))}.dtable-assign-btn.is-unassigned:hover>span{background-color:inherit;color:inherit;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.dtable-assign-btn>.icon{opacity:0;transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.dtable-assign-btn:not(.disabled):hover>.icon{opacity:1}.dtable-header .dtable-cell[data-type=assign]{padding-left:1.5rem}.dtable-cell-icon{order:-5}.dtable-custom-cell[data-type=control]{align-items:center;display:flex;justify-content:center;padding-left:.125rem;padding-right:.125rem;z-index:1}.dtable-empty-tip{min-height:212px}.dtable-cell[data-type=actions] .btn>.icon{font-size:1rem;line-height:1.5rem}.menu.menu-dtable-actions.has-icons{align-items:center;display:flex;gap:.25rem;min-width:auto;--menu-icon-opacity:1}.menu.menu-dtable-actions.has-icons>.menu-item>a{padding-left:.25rem;padding-right:.25rem}.menu.menu-dtable-actions.has-icons>.menu-item>a>.icon-EMPTY,.menu.menu-dtable-actions.has-icons>.menu-item>a>.item-content{display:none}.menu.menu-dtable-actions.has-icons>.menu-item>a>.icon-EMPTY+.item-content,.menu.menu-dtable-actions.has-icons>.menu-item>a>.item-content:first-child{display:block}:root{--state-color:rgba(0,0,0,.03);--zt-page-bg:#f8f9fa;--zt-header-height:3rem;--zt-header-bg:var(--color-canvas);--zt-header-color:var(--color-fore);--zt-sidebar-width:160px;--zt-navbar-active-color:var(--color-primary-500)}body{background-color:var(--zt-page-bg)}.page-app{overflow:overlay}.container{min-width:1200px}#header{height:var(--zt-header-height);--tw-shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 3px 8px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);background:var(--zt-header-bg);border-bottom:1px solid var(--zt-header-border,rgba(var(--color-inverse-rgb),.05));box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);color:var(--zt-header-color)}#header>.container{height:100%;position:relative}#heading{align-items:center;bottom:0;display:flex;gap:.25rem;left:1rem;position:absolute;top:0}#heading>.toolbar{margin-left:-.75rem}#heading .dropmenu-btn>.text{max-width:142px}#heading>[data-zui-dropmenu]+div .dropmenu-btn>.text,#heading>[data-zui-dropmenu]+div+div .dropmenu-btn>.text{max-width:78px}#navbar{align-items:center;display:flex;font-size:.875rem;height:100%;justify-content:center;line-height:1.3rem;--state-color:rgba(var(--color-primary-500-rgb),.05);--state-active-opacity:1;--state-active-color:rgba(var(--color-primary-500-rgb),.08)}#navbar>.nav{--nav-active-color:var(--zt-navbar-active-color,var(--color-current));--nav-item-padding:12px;--nav-divider-margin:8px;margin-left:var(--nav-offset-left)}#navbar>.nav>.nav-item>a{height:3rem;padding-left:var(--nav-item-padding);padding-right:var(--nav-item-padding)}#navbar>.nav>.nav-item>a:before{--tw-scale-x:1!important;--tw-scale-y:1!important;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))!important;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important;--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)!important;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)!important}#navbar>.nav>.nav-item>a.active:before{background:var(--state-active-color)!important;opacity:1!important}#navbar>.nav>.divider{align-self:center;height:1.25rem;margin-left:var(--nav-divider-margin);margin-right:var(--nav-divider-margin);opacity:.2}#navbar>.nav.compact{font-size:.8125rem;line-height:1.25rem}#navbar>.nav.compact-extra{font-size:.75rem;line-height:1rem}#toolbar{align-items:center;bottom:0;display:flex;gap:.25rem;position:absolute;right:1rem;top:0}#main{min-height:100vh}#header+#main{min-height:calc(100vh - 3rem)}#main>.container{padding-left:1rem;padding-right:1rem;position:relative}@media (min-width:1400px){#header>.container,#main>.container,#mainNavbar>.container{padding-left:2.5rem;padding-right:2.5rem}#heading{left:2.5rem}#toolbar{right:2.5rem}}#mainNavbar{height:2.75rem;--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}#mainNavbar>.container{position:relative}#mainNavbar .nav{bottom:0;height:2.75rem;justify-content:center;left:0;position:absolute;right:0;top:0}#mainNavbar .nav-item{font-size:.875rem;line-height:1.3rem}#mainNavbar .nav-item>a{border-radius:var(--radius)}#mainMenu{gap:1rem;justify-content:space-between;padding-bottom:.75rem;padding-top:.75rem}#featureBar,#mainMenu{align-items:center;display:flex}#featureBar{flex:1 1 auto;gap:.5rem}#actionBar{gap:.5rem}#actionBar>.nav-divider{margin-left:-.25rem;margin-right:-.25rem}#mainContent{padding-top:.75rem}#mainMenu+#mainContent{padding-top:0}#mainContent.row{align-items:stretch;display:flex;gap:1rem;justify-content:space-between}#mainContent.row>*{flex:1 1 auto;min-width:0}#mainContainer.has-sidebar-left{--gutter-left-size:16px}#mainContainer.is-sidebar-left-collapsed{--gutter-left-size:0}.has-sidebar-animation.has-sidebar-left #mainMenu{transition-duration:.15s;transition-property:padding-left;transition-timing-function:cubic-bezier(.4,0,.2,1)}:root{--modal-radius:var(--radius);--avatar-bg:var(--color-gray-200)}.modal-alert .modal-footer{justify-content:center}.modal-dialog{--tw-bg-opacity:1}.modal-content,.modal-dialog{max-height:calc(100vh - 4rem)}.modal-body,.modal-footer,.modal-header{padding:1.5rem 2rem}.modal-header{padding-right:3rem}.modal-body+.modal-footer,.modal-header+.modal-actions+.modal-body,.modal-header+.modal-body{padding-top:1px}.modal-condensed .modal-body,.modal-condensed .modal-header{padding:1rem 1.25rem}.modal-condensed .modal-actions{top:.75rem}:root{--label-bg:var(--color-gray-100);--label-color:var(--color-gray-800)}.label[class$=-pale],.label[class*="-pale "]{--tw-bg-opacity:0.4;--tw-ring-opacity:0.4}.body-modal .panel-form,.modal-body>.panel-form{margin:0;padding:0}.modal-body>.panel-form>.panel-heading{padding-left:1px;padding-right:1px;padding-top:1px}.modal-body>.panel-form>.panel-body{padding:1px}.modal-body>.form{padding-top:1px}.modal .history,.modal .panel-body>.form{padding:0}:root{--article-p-space:0}.lane-col-join-with-above{position:relative}.lane-col-join-with-above:before{background-color:inherit;left:0;position:absolute;right:0;--tw-content:"";content:var(--tw-content);height:var(--kanban-col-gap-top,var(--kanban-lanes-gap));top:calc(0px - var(--kanban-col-gap-top, var(--kanban-lanes-gap)))}.arrow,.arrow:before{background:inherit;height:calc(var(--arrow-size)*1.4142);position:absolute;width:calc(var(--arrow-size)*1.4142)}.arrow{visibility:hidden;--arrow-size:5px}.arrow:before{border:inherit;content:"";visibility:visible;--tw-rotate:45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.arrow-top,.arrow-up{border-bottom:inherit;border-right:inherit;border-width:1px 0 0 1px;top:calc(1px - var(--arrow-size))}.white>.arrow-top,.white>.arrow-up{top:calc(0px - var(--arrow-size))}.arrow-bottom,.arrow-down{border-left:inherit;border-top:inherit;border-width:0 1px 1px 0;bottom:calc(1px - var(--arrow-size))}.white>.arrow-bottom,.white>.arrow-down{bottom:calc(0px - var(--arrow-size))}.arrow-left{border-right:inherit;border-top:inherit;border-width:0 0 1px 1px;left:calc(1px - var(--arrow-size))}.white>.arrow-left{left:calc(0px - var(--arrow-size))}.arrow-right{border-bottom:inherit;border-left:inherit;border-width:1px 1px 0 0;right:calc(1px - var(--arrow-size))}.white>.arrow-right{right:calc(0px - var(--arrow-size))}.caret,.caret-down,.caret-left,.caret-right,.caret-up{align-items:center;display:inline-flex;height:.75rem;justify-content:center;position:relative;width:.75rem}.caret-down:before,.caret-left:before,.caret-right:before,.caret-up:before,.caret:before{display:block;height:.35rem;transform-origin:center;width:.35rem;--tw-translate-y:-1px;--tw-rotate:45deg;border-color:currentColor;border-width:0 1px 1px 0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-content:"";content:var(--tw-content)}.caret-left:before{--tw-translate-x:0.125rem;--tw-translate-y:0px;--tw-rotate:135deg}.caret-left:before,.caret-right:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.caret-right:before{--tw-translate-x:-0.125rem;--tw-translate-y:0px;--tw-rotate:-45deg}.caret-up:before{--tw-translate-y:0.125rem;--tw-rotate:225deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}:focus>.caret,:hover>.caret{opacity:inherit}.disabled .caret,.readonly .caret{opacity:var(--caret-opacity,.5)}:root{--toggle-icon-size:calc(var(--font-size-root)*3/4 + 1px)}.toggle-icon,.toggle-icon-collapse,.toggle-icon-expand{border-color:currentColor;border-width:1px;display:block;opacity:.5;position:relative}.toggle-icon-collapse:hover,.toggle-icon-expand:hover,.toggle-icon:hover{opacity:.9}.toggle-icon,.toggle-icon-collapse,.toggle-icon-expand{border-radius:inherit;height:var(--toggle-icon-size);width:var(--toggle-icon-size)}.is-collapsed .toggle-icon:after,.is-collapsed .toggle-icon:before,.is-expanded .toggle-icon:before,.toggle-icon-collapse:after,.toggle-icon-collapse:before,.toggle-icon-expand:before{background-color:currentColor;content:" ";display:block;height:1px;left:2px;position:absolute;right:2px;top:calc((var(--toggle-icon-size) - 3px)/2)}.is-collapsed .toggle-icon:after,.toggle-icon-collapse:after{--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.close{aspect-ratio:1/1;display:block;position:relative;width:1em}.close:after,.close:before{content:"";display:block;height:1px;left:0;position:absolute;width:100%;--tw-rotate:45deg;background-color:currentColor;border-radius:var(--radius-full);top:calc(50% - .5px)}.close:after,.close:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.close:after{--tw-rotate:-45deg}.spinner,.spinner:after,.spinner:before{display:block;height:1em;position:relative;width:1em}.spinner:after,.spinner:before{border-radius:var(--radius-full);content:" ";position:absolute}.spinner:before{animation:-spin 1s cubic-bezier(.6,0,.4,1) infinite;border:.16667em solid transparent;border-top-color:currentcolor}.spinner:after{border:.16667em solid;opacity:.2}.magnifier{display:inline-block;height:1em;position:relative;width:1em}.magnifier:after,.magnifier:before{border-radius:var(--radius-full);content:"";display:block;position:absolute}.magnifier:before{border:1px solid;height:.75em;width:.75em}.magnifier:after{height:1px;left:.55em;top:.7em;width:.5em;--tw-rotate:45deg;background-color:currentColor;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.more,.more-vert{margin:.5rem;position:relative}.more,.more-vert,.more-vert:after,.more-vert:before,.more:after,.more:before{background-color:currentColor;border-radius:var(--radius-full);height:.125rem;width:.125rem}.more-vert:after,.more-vert:before,.more:after,.more:before{display:block;left:-5px;position:absolute;top:0;--tw-content:"";content:var(--tw-content)}.more-vert:after,.more:after{left:5px}.more-vert{--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.chevron-down,.chevron-left,.chevron-right,.chevron-up{align-items:center;display:inline-flex;height:1em;justify-content:center;position:relative;width:1em}.chevron-down:before,.chevron-left:before,.chevron-right:before,.chevron-up:before{display:block;height:.6923077em;transform-origin:center;width:.6923077em;--tw-translate-y:-0.125rem;--tw-rotate:45deg;border-color:currentColor;border-width:0 1px 1px 0;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-content:"";content:var(--tw-content)}.chevron-left:before{--tw-translate-y:0px;--tw-translate-x:0.125rem;--tw-rotate:135deg}.chevron-left:before,.chevron-right:before{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.chevron-right:before{--tw-translate-y:0px;--tw-translate-x:-0.125rem;--tw-rotate:-45deg}.chevron-up:before{--tw-translate-y:0.125rem;--tw-rotate:225deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.i-time{border-color:currentColor;border-radius:50%;border-width:.08em;display:inline-block;height:1em;position:relative;width:1em}.i-time:after{border-color:currentColor;border-width:0 0 .08em .08em;height:.385em;left:.3846em;position:absolute;top:.08em;width:.385em;--tw-content:"";content:var(--tw-content)}.i-calendar{border-color:currentColor;border-radius:2px;border-width:.08em;display:inline-block;height:.79em;margin-top:.16em;position:relative;width:1em}.i-calendar:after,.i-calendar:before{border-color:currentColor;border-width:.08em;position:absolute;--tw-content:"";content:var(--tw-content)}.i-calendar:before{border-bottom-width:0;left:.15em;top:.307em;width:.6em}.i-calendar:after{border-bottom-width:0;border-top-width:0;height:.3077em;left:.17em;top:-.154em;width:.538em}:root{--state-color:rgba(0,0,0,.05);--state-scale:.9;--opacity-disabled:.7;--opacity-muted:.5;--load-indicator-duration:300ms;--load-indicator-delay:.5s}.btn,.item>a,.state{cursor:pointer;position:relative}.btn:before,.item>a:before,.state:before{background:var(--state-color);border-radius:inherit;bottom:0;content:" ";display:block;left:0;opacity:0;pointer-events:none;position:absolute;right:0;top:0;transform:scale(var(--state-scale));transition-duration:.2s;transition-property:opacity,transform,box-shadow,background-color;transition-timing-function:cubic-bezier(.4,0,.2,1)}.btn.active:not(:hover,:focus-visible,:focus-within,:active):before,.item>a.active:not(:hover,:focus-visible,:focus-within,:active):before,.state.active:not(:hover,:focus-visible,:focus-within,:active):before{background:var(--state-active-color,var(--state-color));opacity:var(--state-active-opacity,0);--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));--tw-shadow:inset 0 2px 4px 0 rgba(0,0,0,.05);--tw-shadow-colored:inset 0 2px 4px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);transition-duration:.7s}.btn.selected:not(:hover,:focus-visible,:focus-within,:active):before,.item>a.selected:not(:hover,:focus-visible,:focus-within,:active):before,.state.selected:not(:hover,:focus-visible,:focus-within,:active):before{background:var(--state-selected-color,var(--state-scale));opacity:var(--state-selected-opacity,.1);--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.btn:focus-visible,.item>a:focus-visible,.state:focus-visible{outline:2px solid transparent;outline-offset:2px}.btn:active:before,.btn:focus-visible:before,.btn:hover:before,.item>a:active:before,.item>a:focus-visible:before,.item>a:hover:before,.state:active:before,.state:focus-visible:before,.state:hover:before{--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.btn:hover:before,.item>a:hover:before,.state:hover:before{opacity:var(--state-hover-opacity,1)}.btn:active:before,.item>a:active:before,.state:active:before{background:var(--state-pressed-color,var(--state-color));opacity:var(--state-pressed-opacity,.6);--tw-ring-color:var(--state-ring-color,currentColor);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);transition-duration:.7s}.btn:focus-visible:before,.item>a:focus-visible:before,.state:focus-visible:before{background:var(--state-focus-color,var(--state-color));opacity:var(--state-focus-opacity,1);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-focus-rgb),var(--tw-ring-opacity))}.btn.disabled:before,.btn[disabled]:before,.item>a.disabled:before,.item>a[disabled]:before,.state.disabled:before,.state[disabled]:before{display:none}.btn{--state-active-opacity:1}@-moz-document url-prefix(){.btn,:root{--state-active-opacity:0;--state-focus-opacity:0}}.disabled,.disabled *,[disabled],[disabled] *{cursor:not-allowed!important;opacity:var(--opacity-disabled);--tw-grayscale:grayscale(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.load-indicator:after,.load-indicator:before{display:block;opacity:0;position:absolute;transition-delay:0s;transition-duration:.15s;transition-duration:var(--load-indicator-duration);transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);visibility:hidden}.load-indicator:before{align-items:center;background-color:rgba(var(--color-canvas-rgb),.7);border-radius:inherit;bottom:0;display:flex;font-size:.75rem;justify-content:center;left:0;line-height:1rem;right:0;top:0;z-index:99;--tw-content:attr(data-loading);content:var(--tw-content)}.load-indicator[data-loading]:before{padding-top:3.5rem}.load-indicator:after{border-color:transparent currentcolor currentcolor;border-radius:var(--radius-full);border-style:dotted;border-width:4px 3px 4px 4px;height:2rem;left:50%;margin-left:-1rem;margin-top:-1rem;top:50%;width:2rem;z-index:100;--tw-content:"";content:var(--tw-content)}.load-indicator.loading:after,.load-indicator.loading:before{opacity:1;transition-delay:var(--load-indicator-delay);visibility:visible}@keyframes -spin{to{transform:rotate(1turn)}}.load-indicator.loading:after{animation:-spin 1s linear infinite;opacity:.5}.events-none,.pointer-events-none{pointer-events:none}.events-auto,.pointer-events-auto{pointer-events:auto}.scroll-auto{scroll-behavior:auto}.scroll-smooth{scroll-behavior:smooth}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.select-text{-webkit-user-select:text;-moz-user-select:text;user-select:text}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.select-auto{-webkit-user-select:auto;-moz-user-select:auto;user-select:auto}.cursor-auto{cursor:auto}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.cursor-wait{cursor:wait}.cursor-text{cursor:text}.cursor-move{cursor:move}.cursor-help{cursor:help}.cursor-not-allowed{cursor:not-allowed}.active\:opacity-100.active,.active\:opacity-100:active,.group.active .group-active\:opacity-100,.group:active .group-active\:opacity-100,.group:hover .group-hover\:opacity-100,.hover\:opacity-100:hover{opacity:1}.active\:text-primary.active,.active\:text-primary:active,.group.active .group-active\:text-primary,.group:active .group-active\:text-primary,.group:hover .group-hover\:text-primary,.hover\:text-primary:hover{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.active\:border-primary.active,.active\:border-primary:active,.group.active .group-active\:border-primary,.group:active .group-active\:border-primary,.group:hover .group-hover\:border-primary,.hover\:border-primary:hover{--tw-border-opacity:1;border-color:rgba(var(--color-primary-500-rgb),var(--tw-border-opacity))}.active\:primary-pale.active,.active\:primary-pale:active,.group.active .group-active\:primary-pale,.group:active .group-active\:primary-pale,.group:hover .group-hover\:primary-pale,.hover\:primary-pale:hover{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity))}.active\:primary.active,.active\:primary:active,.group.active .group-active\:primary,.group:active .group-active\:primary,.group:hover .group-hover\:primary,.hover\:primary:hover{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.active\:canvas.active,.active\:canvas:active,.group.active .group-active\:canvas,.group:active .group-active\:canvas,.group:hover .group-hover\:canvas,.hover\:canvas:hover{--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.active\:font-bold.active,.active\:font-bold:active,.group.active .group-active\:font-bold,.group:active .group-active\:font-bold,.group:hover .group-hover\:font-bold,.hover\:font-bold:hover{font-weight:700}.active\:shadow.active,.active\:shadow:active,.group.active .group-active\:shadow,.group:active .group-active\:shadow,.group:hover .group-hover\:shadow,.hover\:shadow:hover{--tw-shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 3px 8px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.aspect-auto{aspect-ratio:auto}.aspect-square{aspect-ratio:1/1}.aspect-video{aspect-ratio:16/9}.container{width:100%}@media (min-width:1720px){.container{max-width:1720px}}.container{margin-left:auto;margin-right:auto}.popup{background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity));border-radius:var(--radius);--tw-bg-opacity:0.85;--tw-shadow:0 10px 25px -5px rgba(0,0,0,.2),0 4px 10px -6px rgba(0,0,0,.15);--tw-shadow-colored:0 10px 25px -5px var(--tw-shadow-color),0 4px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow);--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.05;--tw-backdrop-blur:blur(16px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.table-cell{display:table-cell}.table-row{display:table-row}.hidden{display:none!important}.pull-right{float:right}.pull-left{float:left}.clearfix:after{clear:both;content:"";display:block}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.object-fill{-o-object-fit:fill;object-fit:fill}.object-none{-o-object-fit:none;object-fit:none}.object-scale-down{-o-object-fit:scale-down;object-fit:scale-down}.object-bottom{-o-object-position:bottom;object-position:bottom}.object-center{-o-object-position:center;object-position:center}.object-left{-o-object-position:left;object-position:left}.object-left-bottom{-o-object-position:left bottom;object-position:left bottom}.object-left-top{-o-object-position:left top;object-position:left top}.object-right{-o-object-position:right;object-position:right}.object-right-bottom{-o-object-position:right bottom;object-position:right bottom}.object-right-top{-o-object-position:right top;object-position:right top}.object-top{-o-object-position:top;object-position:top}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-clip{overflow:clip}.overflow-visible{overflow:visible}.overflow-scroll{overflow:scroll}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-x-hidden{overflow-x:hidden}.overflow-y-hidden{overflow-y:hidden}.overflow-x-clip{overflow-x:clip}.overflow-y-clip{overflow-y:clip}.overflow-x-visible{overflow-x:visible}.overflow-y-visible{overflow-y:visible}.overflow-x-scroll{overflow-x:scroll}.overflow-y-scroll{overflow-y:scroll}.overflow-overlay{overflow:overlay}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{bottom:0;left:0;right:0;top:0}.inset-auto{bottom:auto;left:auto;right:auto;top:auto}.inset-x-0{left:0;right:0}.inset-y-0{bottom:0;top:0}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}.left-0{left:0}.top-full{top:100%}.right-full{right:100%}.bottom-full{bottom:100%}.left-full{left:100%}.top-auto{top:auto}.right-auto{right:auto}.bottom-auto{bottom:auto}.left-auto{left:auto}.top-px{top:1px}.right-px{right:1px}.bottom-px{bottom:1px}.left-px{left:1px}.top-0\.5{top:.125rem}.right-0\.5{right:.125rem}.bottom-0\.5{bottom:.125rem}.left-0\.5{left:.125rem}.top-1{top:.25rem}.right-1{right:.25rem}.bottom-1{bottom:.25rem}.left-1{left:.25rem}.top-1\.5{top:.375rem}.right-1\.5{right:.375rem}.bottom-1\.5{bottom:.375rem}.left-1\.5{left:.375rem}.top-2{top:.5rem}.right-2{right:.5rem}.bottom-2{bottom:.5rem}.left-2{left:.5rem}.top-2\.5{top:.625rem}.right-2\.5{right:.625rem}.bottom-2\.5{bottom:.625rem}.left-2\.5{left:.625rem}.top-3{top:.75rem}.right-3{right:.75rem}.bottom-3{bottom:.75rem}.left-3{left:.75rem}.top-3\.5{top:.875rem}.right-3\.5{right:.875rem}.bottom-3\.5{bottom:.875rem}.left-3\.5{left:.875rem}.top-4{top:1rem}.right-4{right:1rem}.bottom-4{bottom:1rem}.left-4{left:1rem}.top-5{top:1.25rem}.right-5{right:1.25rem}.bottom-5{bottom:1.25rem}.left-5{left:1.25rem}.top-6{top:1.5rem}.right-6{right:1.5rem}.bottom-6{bottom:1.5rem}.left-6{left:1.5rem}.top-7{top:1.75rem}.right-7{right:1.75rem}.bottom-7{bottom:1.75rem}.left-7{left:1.75rem}.top-8{top:2rem}.right-8{right:2rem}.bottom-8{bottom:2rem}.left-8{left:2rem}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-0{z-index:0}.z-1{z-index:1}.z-5{z-index:5}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-auto{z-index:auto}.z-base{z-index:1100}.z-above{z-index:50}.z-below{z-index:-50}.z-dropdown{z-index:1200}.z-sticky{z-index:1300}.z-fixed{z-index:1400}.z-modal{z-index:1500}.z-overlay{z-index:1600}.z-popover{z-index:1700}.z-tooltip{z-index:1800}.z-messager{z-index:1900}.m-0{margin:0}.m-auto{margin:auto}.m-px{margin:1px}.m-0\.5{margin:.125rem}.m-1{margin:.25rem}.m-1\.5{margin:.375rem}.m-2{margin:.5rem}.m-2\.5{margin:.625rem}.m-3{margin:.75rem}.m-3\.5{margin:.875rem}.m-4{margin:1rem}.m-5{margin:1.25rem}.m-6{margin:1.5rem}.m-7{margin:1.75rem}.m-8{margin:2rem}.m-9{margin:2.25rem}.m-10{margin:2.5rem}.mx-0{margin-left:0;margin-right:0}.my-0{margin-bottom:0}.mt-0,.my-0{margin-top:0}.mr-0{margin-right:0}.mb-0{margin-bottom:0}.ml-0{margin-left:0}.mx-auto{margin-left:auto;margin-right:auto}.my-auto{margin-bottom:auto}.mt-auto,.my-auto{margin-top:auto}.mr-auto{margin-right:auto}.mb-auto{margin-bottom:auto}.ml-auto{margin-left:auto}.mx-px{margin-left:1px;margin-right:1px}.my-px{margin-bottom:1px}.mt-px,.my-px{margin-top:1px}.mr-px{margin-right:1px}.mb-px{margin-bottom:1px}.ml-px{margin-left:1px}.mx-0\.5{margin-left:.125rem;margin-right:.125rem}.my-0\.5{margin-bottom:.125rem}.mt-0\.5,.my-0\.5{margin-top:.125rem}.mr-0\.5{margin-right:.125rem}.mb-0\.5{margin-bottom:.125rem}.ml-0\.5{margin-left:.125rem}.mx-1{margin-left:.25rem;margin-right:.25rem}.my-1{margin-bottom:.25rem}.mt-1,.my-1{margin-top:.25rem}.mr-1{margin-right:.25rem}.mb-1{margin-bottom:.25rem}.ml-1{margin-left:.25rem}.mx-1\.5{margin-left:.375rem;margin-right:.375rem}.my-1\.5{margin-bottom:.375rem}.mt-1\.5,.my-1\.5{margin-top:.375rem}.mr-1\.5{margin-right:.375rem}.mb-1\.5{margin-bottom:.375rem}.ml-1\.5{margin-left:.375rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.my-2{margin-bottom:.5rem}.mt-2,.my-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.mb-2{margin-bottom:.5rem}.ml-2{margin-left:.5rem}.mx-2\.5{margin-left:.625rem;margin-right:.625rem}.my-2\.5{margin-bottom:.625rem}.mt-2\.5,.my-2\.5{margin-top:.625rem}.mr-2\.5{margin-right:.625rem}.mb-2\.5{margin-bottom:.625rem}.ml-2\.5{margin-left:.625rem}.mx-3{margin-left:.75rem;margin-right:.75rem}.my-3{margin-bottom:.75rem}.mt-3,.my-3{margin-top:.75rem}.mr-3{margin-right:.75rem}.mb-3{margin-bottom:.75rem}.ml-3{margin-left:.75rem}.mx-3\.5{margin-left:.875rem;margin-right:.875rem}.my-3\.5{margin-bottom:.875rem}.mt-3\.5,.my-3\.5{margin-top:.875rem}.mr-3\.5{margin-right:.875rem}.mb-3\.5{margin-bottom:.875rem}.ml-3\.5{margin-left:.875rem}.mx-4{margin-left:1rem;margin-right:1rem}.my-4{margin-bottom:1rem}.mt-4,.my-4{margin-top:1rem}.mr-4{margin-right:1rem}.mb-4{margin-bottom:1rem}.ml-4{margin-left:1rem}.mx-5{margin-left:1.25rem;margin-right:1.25rem}.my-5{margin-bottom:1.25rem}.mt-5,.my-5{margin-top:1.25rem}.mr-5{margin-right:1.25rem}.mb-5{margin-bottom:1.25rem}.ml-5{margin-left:1.25rem}.mx-6{margin-left:1.5rem;margin-right:1.5rem}.my-6{margin-bottom:1.5rem}.mt-6,.my-6{margin-top:1.5rem}.mr-6{margin-right:1.5rem}.mb-6{margin-bottom:1.5rem}.ml-6{margin-left:1.5rem}.mx-7{margin-left:1.75rem;margin-right:1.75rem}.my-7{margin-bottom:1.75rem}.mt-7,.my-7{margin-top:1.75rem}.mr-7{margin-right:1.75rem}.mb-7{margin-bottom:1.75rem}.ml-7{margin-left:1.75rem}.mx-8{margin-left:2rem;margin-right:2rem}.my-8{margin-bottom:2rem}.mt-8,.my-8{margin-top:2rem}.mr-8{margin-right:2rem}.mb-8{margin-bottom:2rem}.ml-8{margin-left:2rem}.mx-9{margin-left:2.25rem;margin-right:2.25rem}.my-9{margin-bottom:2.25rem}.mt-9,.my-9{margin-top:2.25rem}.mr-9{margin-right:2.25rem}.mb-9{margin-bottom:2.25rem}.ml-9{margin-left:2.25rem}.mx-10{margin-left:2.5rem;margin-right:2.5rem}.my-10{margin-bottom:2.5rem}.mt-10,.my-10{margin-top:2.5rem}.mr-10{margin-right:2.5rem}.mb-10{margin-bottom:2.5rem}.ml-10{margin-left:2.5rem}.p-0{padding:0}.p-px{padding:1px}.p-0\.5{padding:.125rem}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-3\.5{padding:.875rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-7{padding:1.75rem}.p-8{padding:2rem}.p-9{padding:2.25rem}.p-10{padding:2.5rem}.px-0{padding-left:0;padding-right:0}.py-0{padding-bottom:0}.pt-0,.py-0{padding-top:0}.pr-0{padding-right:0}.pb-0{padding-bottom:0}.pl-0{padding-left:0}.px-px{padding-left:1px;padding-right:1px}.py-px{padding-bottom:1px}.pt-px,.py-px{padding-top:1px}.pr-px{padding-right:1px}.pb-px{padding-bottom:1px}.pl-px{padding-left:1px}.px-0\.5{padding-left:.125rem;padding-right:.125rem}.py-0\.5{padding-bottom:.125rem}.pt-0\.5,.py-0\.5{padding-top:.125rem}.pr-0\.5{padding-right:.125rem}.pb-0\.5{padding-bottom:.125rem}.pl-0\.5{padding-left:.125rem}.px-1{padding-left:.25rem;padding-right:.25rem}.py-1{padding-bottom:.25rem}.pt-1,.py-1{padding-top:.25rem}.pr-1{padding-right:.25rem}.pb-1{padding-bottom:.25rem}.pl-1{padding-left:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.py-1\.5{padding-bottom:.375rem}.pt-1\.5,.py-1\.5{padding-top:.375rem}.pr-1\.5{padding-right:.375rem}.pb-1\.5{padding-bottom:.375rem}.pl-1\.5{padding-left:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.py-2{padding-bottom:.5rem}.pt-2,.py-2{padding-top:.5rem}.pr-2{padding-right:.5rem}.pb-2{padding-bottom:.5rem}.pl-2{padding-left:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.py-2\.5{padding-bottom:.625rem}.pt-2\.5,.py-2\.5{padding-top:.625rem}.pr-2\.5{padding-right:.625rem}.pb-2\.5{padding-bottom:.625rem}.pl-2\.5{padding-left:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-3{padding-bottom:.75rem}.pt-3,.py-3{padding-top:.75rem}.pr-3{padding-right:.75rem}.pb-3{padding-bottom:.75rem}.pl-3{padding-left:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.py-3\.5{padding-bottom:.875rem}.pt-3\.5,.py-3\.5{padding-top:.875rem}.pr-3\.5{padding-right:.875rem}.pb-3\.5{padding-bottom:.875rem}.pl-3\.5{padding-left:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.py-4{padding-bottom:1rem}.pt-4,.py-4{padding-top:1rem}.pr-4{padding-right:1rem}.pb-4{padding-bottom:1rem}.pl-4{padding-left:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.py-5{padding-bottom:1.25rem}.pt-5,.py-5{padding-top:1.25rem}.pr-5{padding-right:1.25rem}.pb-5{padding-bottom:1.25rem}.pl-5{padding-left:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-6{padding-bottom:1.5rem}.pt-6,.py-6{padding-top:1.5rem}.pr-6{padding-right:1.5rem}.pb-6{padding-bottom:1.5rem}.pl-6{padding-left:1.5rem}.px-7{padding-left:1.75rem;padding-right:1.75rem}.py-7{padding-bottom:1.75rem}.pt-7,.py-7{padding-top:1.75rem}.pr-7{padding-right:1.75rem}.pb-7{padding-bottom:1.75rem}.pl-7{padding-left:1.75rem}.px-8{padding-left:2rem;padding-right:2rem}.py-8{padding-bottom:2rem}.pt-8,.py-8{padding-top:2rem}.pr-8{padding-right:2rem}.pb-8{padding-bottom:2rem}.pl-8{padding-left:2rem}.px-9{padding-left:2.25rem;padding-right:2.25rem}.py-9{padding-bottom:2.25rem}.pt-9,.py-9{padding-top:2.25rem}.pr-9{padding-right:2.25rem}.pb-9{padding-bottom:2.25rem}.pl-9{padding-left:2.25rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.py-10{padding-bottom:2.5rem}.pt-10,.py-10{padding-top:2.5rem}.pr-10{padding-right:2.5rem}.pb-10{padding-bottom:2.5rem}.pl-10{padding-left:2.5rem}.space-x-0>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(0px*(1 - var(--tw-space-x-reverse)));margin-right:calc(0px*var(--tw-space-x-reverse))}.space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(0px*var(--tw-space-y-reverse));margin-top:calc(0px*(1 - var(--tw-space-y-reverse)))}.space-x-0\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.125rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.125rem*var(--tw-space-x-reverse))}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.125rem*var(--tw-space-y-reverse));margin-top:calc(.125rem*(1 - var(--tw-space-y-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.space-x-1\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.375rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.375rem*var(--tw-space-x-reverse))}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.375rem*var(--tw-space-y-reverse));margin-top:calc(.375rem*(1 - var(--tw-space-y-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.space-x-2\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.625rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.625rem*var(--tw-space-x-reverse))}.space-y-2\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.625rem*var(--tw-space-y-reverse));margin-top:calc(.625rem*(1 - var(--tw-space-y-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.75rem*var(--tw-space-x-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.space-x-3\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.875rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.875rem*var(--tw-space-x-reverse))}.space-y-3\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.875rem*var(--tw-space-y-reverse));margin-top:calc(.875rem*(1 - var(--tw-space-y-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.space-x-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.25rem*var(--tw-space-x-reverse))}.space-y-5>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.25rem*var(--tw-space-y-reverse));margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.5rem*var(--tw-space-x-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.5rem*var(--tw-space-y-reverse));margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)))}.space-x-7>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.75rem*var(--tw-space-x-reverse))}.space-y-7>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.75rem*var(--tw-space-y-reverse));margin-top:calc(1.75rem*(1 - var(--tw-space-y-reverse)))}.space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2rem*var(--tw-space-x-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(2rem*var(--tw-space-y-reverse));margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)))}.space-x-9>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2.25rem*var(--tw-space-x-reverse))}.space-y-9>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(2.25rem*var(--tw-space-y-reverse));margin-top:calc(2.25rem*(1 - var(--tw-space-y-reverse)))}.space-x-10>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2.5rem*var(--tw-space-x-reverse))}.space-y-10>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(2.5rem*var(--tw-space-y-reverse));margin-top:calc(2.5rem*(1 - var(--tw-space-y-reverse)))}.space-x-px>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1px*(1 - var(--tw-space-x-reverse)));margin-right:calc(1px*var(--tw-space-x-reverse))}.space-y-px>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1px*var(--tw-space-y-reverse));margin-top:calc(1px*(1 - var(--tw-space-y-reverse)))}.w-0{width:0}.w-px{width:1px}.w-0\.5{width:.125rem}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-9{width:2.25rem}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-20{width:5rem}.w-24{width:6rem}.w-28{width:7rem}.w-32{width:8rem}.w-36{width:9rem}.w-40{width:10rem}.w-44{width:11rem}.w-48{width:12rem}.w-52{width:13rem}.w-56{width:14rem}.w-60{width:15rem}.w-64{width:16rem}.w-72{width:18rem}.w-80{width:20rem}.w-96{width:24rem}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-2\/3{width:66.666667%}.w-1\/4{width:25%}.w-2\/4{width:50%}.w-3\/4{width:75%}.w-1\/5{width:20%}.w-2\/5{width:40%}.w-3\/5{width:60%}.w-4\/5{width:80%}.w-1\/6{width:16.666667%}.w-2\/6{width:33.333333%}.w-3\/6{width:50%}.w-4\/6{width:66.666667%}.w-5\/6{width:83.333333%}.w-auto{width:auto}.w-full{width:100%}.w-screen{width:100vw}.w-min{width:-moz-min-content;width:min-content}.w-max{width:-moz-max-content;width:max-content}.w-fit{width:-moz-fit-content;width:fit-content}.min-w-0{min-width:0}.min-w-full{min-width:100%}.min-w-min{min-width:-moz-min-content;min-width:min-content}.min-w-max{min-width:-moz-max-content;min-width:max-content}.min-w-fit{min-width:-moz-fit-content;min-width:fit-content}.max-w-0{max-width:0}.max-w-none{max-width:none}.max-w-xs{max-width:20rem}.max-w-sm{max-width:24rem}.max-w-md{max-width:28rem}.max-w-lg{max-width:32rem}.max-w-xl{max-width:36rem}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-full{max-width:100%}.max-w-min{max-width:-moz-min-content;max-width:min-content}.max-w-max{max-width:-moz-max-content;max-width:max-content}.max-w-fit{max-width:-moz-fit-content;max-width:fit-content}.h-0{height:0}.h-px{height:1px}.h-0\.5{height:.125rem}.h-1{height:.25rem}.h-1\.5{height:.375rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-20{height:5rem}.h-24{height:6rem}.h-28{height:7rem}.h-32{height:8rem}.h-36{height:9rem}.h-40{height:10rem}.h-44{height:11rem}.h-48{height:12rem}.h-52{height:13rem}.h-56{height:14rem}.h-60{height:15rem}.h-64{height:16rem}.h-72{height:18rem}.h-80{height:20rem}.h-96{height:24rem}.h-1\/2{height:50%}.h-1\/3{height:33.333333%}.h-2\/3{height:66.666667%}.h-1\/4{height:25%}.h-2\/4{height:50%}.h-3\/4{height:75%}.h-1\/5{height:20%}.h-2\/5{height:40%}.h-3\/5{height:60%}.h-4\/5{height:80%}.h-1\/6{height:16.666667%}.h-2\/6{height:33.333333%}.h-3\/6{height:50%}.h-4\/6{height:66.666667%}.h-5\/6{height:83.333333%}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.h-min{height:-moz-min-content;height:min-content}.h-max{height:-moz-max-content;height:max-content}.h-fit{height:-moz-fit-content;height:fit-content}.min-h-0{min-height:0}.min-h-full{min-height:100%}.min-h-min{min-height:-moz-min-content;min-height:min-content}.min-h-max{min-height:-moz-max-content;min-height:max-content}.min-h-fit{min-height:-moz-fit-content;min-height:fit-content}.max-h-0{max-height:0}.max-h-px{max-height:1px}.max-h-none{max-height:none}.max-h-full{max-height:100%}.max-h-min{max-height:-moz-min-content;max-height:min-content}.max-h-screen{max-height:100vh}.max-h-max{max-height:-moz-max-content;max-height:max-content}.max-h-fit{max-height:-moz-fit-content;max-height:fit-content}.canvas,.surface,.surface-light,.surface-strong{--tw-text-opacity:1;color:rgba(var(--color-fore-rgb),var(--tw-text-opacity))}.canvas{--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.surface-light{--tw-bg-opacity:1;background-color:rgba(var(--color-surface-light-rgb),var(--tw-bg-opacity));--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-surface-light-rgb),var(--tw-ring-opacity))}.surface{--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity));--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-surface-rgb),var(--tw-ring-opacity))}.surface-strong{--tw-bg-opacity:1;background-color:rgba(var(--color-surface-strong-rgb),var(--tw-bg-opacity));--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-surface-strong-rgb),var(--tw-ring-opacity))}.inverse{background-color:rgba(var(--color-inverse-rgb),var(--tw-bg-opacity));color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.inverse,.white{--tw-bg-opacity:1;--tw-text-opacity:1}.white{background-color:rgba(var(--color-white-rgb),var(--tw-bg-opacity));color:rgba(var(--color-fore-in-light-rgb),var(--tw-text-opacity));--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-white-rgb),var(--tw-ring-opacity))}.black{--tw-bg-opacity:1;background-color:rgba(var(--color-black-rgb),var(--tw-bg-opacity));--tw-text-opacity:1;color:rgba(var(--color-fore-in-dark-rgb),var(--tw-text-opacity));--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-black-rgb),var(--tw-ring-opacity))}.transparent{background-color:transparent;--tw-ring-color:transparent}.skin{--skin-bg:var(--skin-color);--skin-ring:var(--skin-color);--skin-border:var(--skin-color);--skin-hover-text:var(--skin-text);--skin-hover-bg:var(--skin-bg);--tw-ring-color:var(--skin-ring);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);--color-link-hover:var(--skin-text);background-color:var(--skin-bg);border-color:var(--skin-border);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);color:var(--skin-text)}.skin:hover{background-color:var(--skin-hover-bg);color:var(--skin-hover-text)}.danger,.darken,.darker,.darkest,.gray,.gray-100,.gray-200,.gray-300,.gray-400,.gray-50,.gray-500,.gray-600,.gray-700,.gray-800,.gray-900,.gray-950,.important,.lighten,.lighter,.lightest,.primary,.secondary,.special,.success,.warning{--skin-bg:var(--skin-color);--skin-ring:var(--skin-color);--skin-border:var(--skin-color);--skin-hover-text:var(--skin-text);--skin-hover-bg:var(--skin-bg);--tw-ring-color:var(--skin-ring);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);--color-link-hover:var(--skin-text);background-color:var(--skin-bg);border-color:var(--skin-border);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);color:var(--skin-text)}.danger:hover,.darken:hover,.darker:hover,.darkest:hover,.gray-100:hover,.gray-200:hover,.gray-300:hover,.gray-400:hover,.gray-500:hover,.gray-50:hover,.gray-600:hover,.gray-700:hover,.gray-800:hover,.gray-900:hover,.gray-950:hover,.gray:hover,.important:hover,.lighten:hover,.lighter:hover,.lightest:hover,.primary:hover,.secondary:hover,.special:hover,.success:hover,.warning:hover{background-color:var(--skin-hover-bg);color:var(--skin-hover-text)}.danger,.darken,.darker,.darkest,.gray,.gray-100,.gray-200,.gray-300,.gray-400,.gray-50,.gray-500,.gray-600,.gray-700,.gray-800,.gray-900,.gray-950,.important,.lighten,.lighter,.lightest,.primary,.secondary,.special,.success,.warning{--skin-text:rgba(var(--color-fore-in-dark-rgb),var(--tw-text-opacity));--skin-color:rgba(var(--skin-solid-rgb),1);--state-ring-color:var(--skin-color);--state-active-color:rgba(0,0,0,.1);--state-pressed-color:rgba(0,0,0,.1);--state-color:hsla(0,0%,100%,.1);--state-focus-color:rgba(0,0,0,.1)}.primary{--skin-solid-rgb:var(--color-primary-500-rgb)}.secondary{--skin-solid-rgb:var(--color-secondary-500-rgb)}.success{--skin-solid-rgb:var(--color-success-500-rgb)}.warning{--skin-solid-rgb:var(--color-warning-500-rgb)}.danger{--skin-solid-rgb:var(--color-danger-500-rgb)}.important{--skin-solid-rgb:var(--color-important-500-rgb)}.special{--skin-solid-rgb:var(--color-special-500-rgb)}.gray-50{--skin-bg:rgba(var(--color-gray-50-rgb),var(--tw-bg-opacity));--skin-text:rgba(var(--color-fore),var(--tw-text-opacity))}.gray-100{--skin-solid-rgb:var(--color-gray-100-rgb);--skin-text:rgba(var(--color-fore),var(--tw-text-opacity))}.gray-200,.lightest{--skin-solid-rgb:var(--color-gray-200-rgb);--skin-text:rgba(var(--color-fore),var(--tw-text-opacity))}.gray-300,.lighter{--skin-solid-rgb:var(--color-gray-300-rgb);--skin-text:rgba(var(--color-fore),var(--tw-text-opacity))}.gray-400,.lighten{--skin-solid-rgb:var(--color-gray-400-rgb)}.gray,.gray-500{--skin-solid-rgb:var(--color-gray-500-rgb)}.darken,.gray-600{--skin-solid-rgb:var(--color-gray-600-rgb)}.darker,.gray-700{--skin-solid-rgb:var(--color-gray-700-rgb)}.darkest,.gray-800{--skin-solid-rgb:var(--color-gray-800-rgb)}.gray-900{--skin-solid-rgb:var(--color-gray-900-rgb)}.gray-950{--skin-solid-rgb:var(--color-gray-950-rgb)}.danger-pale,.gray-100-pale,.gray-200-pale,.gray-300-pale,.gray-400-pale,.gray-50-pale,.gray-500-pale,.gray-600-pale,.gray-700-pale,.gray-800-pale,.gray-900-pale,.gray-950-pale,.gray-pale,.important-pale,.pale,.primary-pale,.secondary-pale,.special-pale,.success-pale,.warning-pale{--skin-bg:var(--skin-color);--skin-ring:var(--skin-color);--skin-border:var(--skin-color);--skin-hover-text:var(--skin-text);--skin-hover-bg:var(--skin-bg);--tw-ring-color:var(--skin-ring);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);--color-link-hover:var(--skin-text);background-color:var(--skin-bg);border-color:var(--skin-border);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);color:var(--skin-text)}.danger-pale:hover,.gray-100-pale:hover,.gray-200-pale:hover,.gray-300-pale:hover,.gray-400-pale:hover,.gray-50-pale:hover,.gray-500-pale:hover,.gray-600-pale:hover,.gray-700-pale:hover,.gray-800-pale:hover,.gray-900-pale:hover,.gray-950-pale:hover,.gray-pale:hover,.important-pale:hover,.pale:hover,.primary-pale:hover,.secondary-pale:hover,.special-pale:hover,.success-pale:hover,.warning-pale:hover{background-color:var(--skin-hover-bg);color:var(--skin-hover-text)}.danger-pale,.gray-100-pale,.gray-200-pale,.gray-300-pale,.gray-400-pale,.gray-50-pale,.gray-500-pale,.gray-600-pale,.gray-700-pale,.gray-800-pale,.gray-900-pale,.gray-950-pale,.gray-pale,.important-pale,.pale,.primary-pale,.secondary-pale,.special-pale,.success-pale,.warning-pale{--skin-color:rgba(var(--skin-pale-rgb),.1);--skin-text:rgba(var(--skin-pale-rgb),1);--state-color:rgba(var(--skin-pale-rgb),.1)}.pale{--skin-pale-rgb:var(--color-gray-500-rgb)}.primary-pale{--skin-pale-rgb:var(--color-primary-500-rgb)}.secondary-pale{--skin-pale-rgb:var(--color-secondary-500-rgb)}.success-pale{--skin-pale-rgb:var(--color-success-500-rgb)}.warning-pale{--skin-pale-rgb:var(--color-warning-500-rgb)}.danger-pale{--skin-pale-rgb:var(--color-danger-500-rgb)}.important-pale{--skin-pale-rgb:var(--color-important-500-rgb)}.special-pale{--skin-pale-rgb:var(--color-special-500-rgb)}.gray-50-pale{--skin-pale-rgb:var(--color-gray-50-rgb);--skin-text:var(--color-gray-400)}.gray-100-pale{--skin-pale-rgb:var(--color-gray-100-rgb);--skin-text:var(--color-gray-400)}.gray-200-pale{--skin-pale-rgb:var(--color-gray-200-rgb);--skin-text:var(--color-gray-400)}.gray-300-pale{--skin-pale-rgb:var(--color-gray-300-rgb);--skin-text:var(--color-gray-500)}.gray-400-pale{--skin-pale-rgb:var(--color-gray-400-rgb);--skin-text:var(--color-gray-500)}.gray-500-pale,.gray-pale{--skin-pale-rgb:var(--color-gray-500-rgb)}.gray-600-pale{--skin-pale-rgb:var(--color-gray-600-rgb)}.gray-700-pale{--skin-pale-rgb:var(--color-gray-700-rgb)}.gray-800-pale{--skin-pale-rgb:var(--color-gray-800-rgb)}.gray-900-pale{--skin-pale-rgb:var(--color-gray-900-rgb)}.gray-950-pale{--skin-pale-rgb:var(--color-gray-950-rgb)}.danger-outline,.dark-outline,.gray-300-outline,.gray-400-outline,.gray-600-outline,.gray-outline,.important-outline,.light-outline,.outline,.primary-outline,.secondary-outline,.special-outline,.success-outline,.warning-outline{--skin-bg:var(--skin-color);--skin-ring:var(--skin-color);--skin-border:var(--skin-color);--skin-hover-text:var(--skin-text);--skin-hover-bg:var(--skin-bg);--tw-ring-color:var(--skin-ring);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);--color-link-hover:var(--skin-text);background-color:var(--skin-bg);border-color:var(--skin-border);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);color:var(--skin-text)}.danger-outline:hover,.dark-outline:hover,.gray-300-outline:hover,.gray-400-outline:hover,.gray-600-outline:hover,.gray-outline:hover,.important-outline:hover,.light-outline:hover,.outline:hover,.primary-outline:hover,.secondary-outline:hover,.special-outline:hover,.success-outline:hover,.warning-outline:hover{background-color:var(--skin-hover-bg);color:var(--skin-hover-text)}.danger-outline,.dark-outline,.gray-300-outline,.gray-400-outline,.gray-600-outline,.gray-outline,.important-outline,.light-outline,.outline,.primary-outline,.secondary-outline,.special-outline,.success-outline,.warning-outline{--skin-text:var(--skin-color,inherit);--skin-bg:transparent;--skin-color:rgba(var(--skin-outline-rgb),1);--state-color:rgba(var(--skin-outline-rgb),.1)}.outline{--skin-outline-rgb:var(--color-fore-rgb)}.primary-outline{--skin-outline-rgb:var(--color-primary-500-rgb)}.secondary-outline{--skin-outline-rgb:var(--color-secondary-500-rgb)}.success-outline{--skin-outline-rgb:var(--color-success-500-rgb)}.warning-outline{--skin-outline-rgb:var(--color-warning-500-rgb)}.danger-outline{--skin-outline-rgb:var(--color-danger-500-rgb)}.important-outline{--skin-outline-rgb:var(--color-important-500-rgb)}.special-outline{--skin-outline-rgb:var(--color-special-500-rgb)}.gray-300-outline{--skin-text:var(--color-gray-500);--skin-outline-rgb:var(--color-gray-300-rgb)}.gray-400-outline{--skin-text:var(--color-gray-500);--skin-outline-rgb:var(--color-gray-400-rgb)}.gray-outline{--skin-outline-rgb:var(--color-gray-500-rgb)}.gray-600-outline{--skin-outline-rgb:var(--color-gray-600-rgb)}.dark-outline{--skin-outline-rgb:var(--color-inverse-rgb)}.light-outline{--skin-outline-rgb:var(--color-canvas-rgb)}.danger-ghost,.ghost,.important-ghost,.primary-ghost,.secondary-ghost,.special-ghost,.success-ghost,.warning-ghost{--skin-bg:var(--skin-color);--skin-ring:var(--skin-color);--skin-border:var(--skin-color);--skin-hover-text:var(--skin-text);--skin-hover-bg:var(--skin-bg);--tw-ring-color:var(--skin-ring);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);--color-link-hover:var(--skin-text);background-color:var(--skin-bg);border-color:var(--skin-border);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);color:var(--skin-text)}.danger-ghost:hover,.ghost:hover,.important-ghost:hover,.primary-ghost:hover,.secondary-ghost:hover,.special-ghost:hover,.success-ghost:hover,.warning-ghost:hover{background-color:var(--skin-hover-bg);color:var(--skin-hover-text)}.danger-ghost,.ghost,.important-ghost,.primary-ghost,.secondary-ghost,.special-ghost,.success-ghost,.warning-ghost{--tw-ring-opacity:0;--tw-bg-opacity:0;--skin-text:var(--skin-color,inherit);--skin-bg:transparent;--skin-ring:transparent;--skin-color:rgba(var(--skin-ghost-rgb),1);--state-color:rgba(var(--skin-ghost-rgb),.1)}.ghost{--skin-ghost-rgb:var(--color-fore-rgb);--skin-text:inherit}.primary-ghost{--skin-ghost-rgb:var(--color-primary-500-rgb)}.secondary-ghost{--skin-ghost-rgb:var(--color-secondary-500-rgb)}.success-ghost{--skin-ghost-rgb:var(--color-success-500-rgb)}.warning-ghost{--skin-ghost-rgb:var(--color-warning-500-rgb)}.danger-ghost{--skin-ghost-rgb:var(--color-danger-500-rgb)}.important-ghost{--skin-ghost-rgb:var(--color-important-500-rgb)}.special-ghost{--skin-ghost-rgb:var(--color-special-500-rgb)}.font-sans{font-family:-apple-system,Noto Sans,Helvetica Neue,Helvetica,Nimbus Sans L,Arial,Liberation Sans,PingFang SC,Hiragino Sans GB,Noto Sans CJK SC,Source Han Sans SC,Source Han Sans CN,Microsoft YaHei,Wenquanyi Micro Hei,WenQuanYi Zen Hei,ST Heiti,SimHei,WenQuanYi Zen Hei Sharp,sans-serif}.font-serif{font-family:Nimbus Roman No9 L,Songti SC,Noto Serif CJK SC,Source Han Serif SC,Source Han Serif CN,STSong,AR PL New Sung,AR PL SungtiL GB,NSimSun,SimSun,TW-Sung,WenQuanYi Bitmap Song,AR PL UMing CN,AR PL UMing HK,AR PL UMing TW,AR PL UMing TW MBE,PMingLiU,MingLiU,serif}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-root{font-size:16px}.text-sm{font-size:.75rem;line-height:1rem}.text-base{font-size:.8125rem;line-height:1.25rem}.text-md{font-size:.875rem;line-height:1.3rem}.text-lg{font-size:1rem;line-height:1.5rem}.text-xl{font-size:1.125rem;line-height:1.75rem}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.font-thin{font-weight:100}.font-light{font-weight:300}.font-normal{font-weight:400}.font-medium{font-weight:500}.font-semibold{font-weight:600}.font-bold{font-weight:700}.font-black{font-weight:900}.leading-3{line-height:.75rem}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-7{line-height:1.75rem}.leading-8{line-height:2rem}.leading-9{line-height:2.25rem}.leading-10{line-height:2.5rem}.leading-none{line-height:1}.leading-tight{line-height:1.25}.leading-snug{line-height:1.375}.leading-normal{line-height:1.5}.leading-relaxed{line-height:1.625}.leading-loose{line-height:2}.line-clamp-1{-webkit-line-clamp:1}.line-clamp-1,.line-clamp-2{display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical}.line-clamp-2{-webkit-line-clamp:2}.line-clamp-3{-webkit-line-clamp:3}.line-clamp-3,.line-clamp-4{display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical}.line-clamp-4{-webkit-line-clamp:4}.line-clamp-5{-webkit-line-clamp:5}.line-clamp-5,.line-clamp-6{display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical}.line-clamp-6{-webkit-line-clamp:6}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.text-justify{text-align:justify}.align-middle{vertical-align:middle}.align-top{vertical-align:top}.align-bottom{vertical-align:bottom}.align-sub{vertical-align:sub}.align-super{vertical-align:super}.text-primary{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.text-secondary{--tw-text-opacity:1;color:rgba(var(--color-secondary-500-rgb),var(--tw-text-opacity))}.text-success{--tw-text-opacity:1;color:rgba(var(--color-success-500-rgb),var(--tw-text-opacity))}.text-warning{--tw-text-opacity:1;color:rgba(var(--color-warning-500-rgb),var(--tw-text-opacity))}.text-danger{--tw-text-opacity:1;color:rgba(var(--color-danger-500-rgb),var(--tw-text-opacity))}.text-important{--tw-text-opacity:1;color:rgba(var(--color-important-500-rgb),var(--tw-text-opacity))}.text-special{--tw-text-opacity:1;color:rgba(var(--color-special-500-rgb),var(--tw-text-opacity))}.text-primary-50{--tw-text-opacity:1;color:rgba(var(--color-primary-50-rgb),var(--tw-text-opacity))}.text-primary-100{--tw-text-opacity:1;color:rgba(var(--color-primary-100-rgb),var(--tw-text-opacity))}.text-primary-200{--tw-text-opacity:1;color:rgba(var(--color-primary-200-rgb),var(--tw-text-opacity))}.text-primary-300{--tw-text-opacity:1;color:rgba(var(--color-primary-300-rgb),var(--tw-text-opacity))}.text-primary-400{--tw-text-opacity:1;color:rgba(var(--color-primary-400-rgb),var(--tw-text-opacity))}.text-primary-500{--tw-text-opacity:1;color:rgba(var(--color-primary-500-rgb),var(--tw-text-opacity))}.text-primary-600{--tw-text-opacity:1;color:rgba(var(--color-primary-600-rgb),var(--tw-text-opacity))}.text-primary-700{--tw-text-opacity:1;color:rgba(var(--color-primary-700-rgb),var(--tw-text-opacity))}.text-primary-800{--tw-text-opacity:1;color:rgba(var(--color-primary-800-rgb),var(--tw-text-opacity))}.text-primary-900{--tw-text-opacity:1;color:rgba(var(--color-primary-900-rgb),var(--tw-text-opacity))}.text-primary-950{--tw-text-opacity:1;color:rgba(var(--color-primary-950-rgb),var(--tw-text-opacity))}.text-secondary-50{--tw-text-opacity:1;color:rgba(var(--color-secondary-50-rgb),var(--tw-text-opacity))}.text-secondary-100{--tw-text-opacity:1;color:rgba(var(--color-secondary-100-rgb),var(--tw-text-opacity))}.text-secondary-200{--tw-text-opacity:1;color:rgba(var(--color-secondary-200-rgb),var(--tw-text-opacity))}.text-secondary-300{--tw-text-opacity:1;color:rgba(var(--color-secondary-300-rgb),var(--tw-text-opacity))}.text-secondary-400{--tw-text-opacity:1;color:rgba(var(--color-secondary-400-rgb),var(--tw-text-opacity))}.text-secondary-500{--tw-text-opacity:1;color:rgba(var(--color-secondary-500-rgb),var(--tw-text-opacity))}.text-secondary-600{--tw-text-opacity:1;color:rgba(var(--color-secondary-600-rgb),var(--tw-text-opacity))}.text-secondary-700{--tw-text-opacity:1;color:rgba(var(--color-secondary-700-rgb),var(--tw-text-opacity))}.text-secondary-800{--tw-text-opacity:1;color:rgba(var(--color-secondary-800-rgb),var(--tw-text-opacity))}.text-secondary-900{--tw-text-opacity:1;color:rgba(var(--color-secondary-900-rgb),var(--tw-text-opacity))}.text-secondary-950{--tw-text-opacity:1;color:rgba(var(--color-secondary-950-rgb),var(--tw-text-opacity))}.text-gray-50{--tw-text-opacity:1;color:rgba(var(--color-gray-50-rgb),var(--tw-text-opacity))}.text-gray-100{--tw-text-opacity:1;color:rgba(var(--color-gray-100-rgb),var(--tw-text-opacity))}.text-gray-200,.text-lightest{--tw-text-opacity:1;color:rgba(var(--color-gray-200-rgb),var(--tw-text-opacity))}.text-gray-300,.text-lighter{--tw-text-opacity:1;color:rgba(var(--color-gray-300-rgb),var(--tw-text-opacity))}.text-gray-400,.text-lighten{--tw-text-opacity:1;color:rgba(var(--color-gray-400-rgb),var(--tw-text-opacity))}.text-gray,.text-gray-500{--tw-text-opacity:1;color:rgba(var(--color-gray-500-rgb),var(--tw-text-opacity))}.text-darken,.text-gray-600{--tw-text-opacity:1;color:rgba(var(--color-gray-600-rgb),var(--tw-text-opacity))}.text-darker,.text-gray-700{--tw-text-opacity:1;color:rgba(var(--color-gray-700-rgb),var(--tw-text-opacity))}.text-darkest,.text-gray-800{--tw-text-opacity:1;color:rgba(var(--color-gray-800-rgb),var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgba(var(--color-gray-900-rgb),var(--tw-text-opacity))}.text-gray-950{--tw-text-opacity:1;color:rgba(var(--color-gray-950-rgb),var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgba(var(--color-white-rgb),var(--tw-text-opacity))}.text-black{--tw-text-opacity:1;color:rgba(var(--color-black-rgb),var(--tw-text-opacity))}.text-transparent{color:transparent}.text-canvas{--tw-text-opacity:1;color:rgba(var(--color-canvas-rgb),var(--tw-text-opacity))}.text-surface-light{--tw-text-opacity:1;color:rgba(var(--color-surface-light-rgb),var(--tw-text-opacity))}.text-surface{--tw-text-opacity:1;color:rgba(var(--color-surface-rgb),var(--tw-text-opacity))}.text-surface-strong{--tw-text-opacity:1;color:rgba(var(--color-surface-strong-rgb),var(--tw-text-opacity))}.text-inverse{--tw-text-opacity:1;color:rgba(var(--color-inverse-rgb),var(--tw-text-opacity))}.text-fore-in-light{--tw-text-opacity:1;color:rgba(var(--color-fore-in-light-rgb),var(--tw-text-opacity))}.text-fore-in-dark{--tw-text-opacity:1;color:rgba(var(--color-fore-in-dark-rgb),var(--tw-text-opacity))}.text-fore{--tw-text-opacity:1;color:rgba(var(--color-fore-rgb),var(--tw-text-opacity))}.text-focus{--tw-text-opacity:1;color:rgba(var(--color-focus-rgb),var(--tw-text-opacity))}.text-link{--tw-text-opacity:1;color:rgba(var(--color-link-rgb),var(--tw-text-opacity))}.text-link-hover{--tw-text-opacity:1;color:rgba(var(--color-link-hover-rgb),var(--tw-text-opacity))}.text-current{color:currentColor}.text-inherit{color:inherit}.clip,.ellipsis,.text-clip,.text-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.clip,.text-clip{text-overflow:clip}.whitespace-normal{white-space:normal}.nowrap,.whitespace-nowrap{white-space:nowrap}.pre,.whitespace-pre{white-space:pre}.pre-line,.whitespace-pre-line{white-space:pre-line}.pre-wrap,.whitespace-pre-wrap{white-space:pre-wrap}.break-normal{overflow-wrap:normal;word-break:normal}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.text-opacity-0{--tw-text-opacity:0}.text-opacity-5{--tw-text-opacity:.05}.text-opacity-10{--tw-text-opacity:.1}.text-opacity-20{--tw-text-opacity:.2}.text-opacity-25{--tw-text-opacity:.25}.text-opacity-30{--tw-text-opacity:.3}.text-opacity-40{--tw-text-opacity:.4}.text-opacity-50{--tw-text-opacity:.5}.text-opacity-60{--tw-text-opacity:.6}.text-opacity-70{--tw-text-opacity:.7}.text-opacity-80{--tw-text-opacity:.8}.text-opacity-90{--tw-text-opacity:.9}.text-opacity-95{--tw-text-opacity:.95}.text-opacity-100{--tw-text-opacity:1}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.normal-case{text-transform:none}.list-none{list-style-type:none}.list-disc{list-style-type:disc}.list-decimal{list-style-type:decimal}.list-unstyled{list-style-type:none;padding-left:0}.underline{text-decoration-line:underline}.overline{text-decoration-line:overline}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.bg-primary{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity))}.bg-secondary{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-500-rgb),var(--tw-bg-opacity))}.bg-success{--tw-bg-opacity:1;background-color:rgba(var(--color-success-500-rgb),var(--tw-bg-opacity))}.bg-warning{--tw-bg-opacity:1;background-color:rgba(var(--color-warning-500-rgb),var(--tw-bg-opacity))}.bg-danger{--tw-bg-opacity:1;background-color:rgba(var(--color-danger-500-rgb),var(--tw-bg-opacity))}.bg-important{--tw-bg-opacity:1;background-color:rgba(var(--color-important-500-rgb),var(--tw-bg-opacity))}.bg-special{--tw-bg-opacity:1;background-color:rgba(var(--color-special-500-rgb),var(--tw-bg-opacity))}.bg-primary-50{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-50-rgb),var(--tw-bg-opacity))}.bg-primary-100{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-100-rgb),var(--tw-bg-opacity))}.bg-primary-200{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-200-rgb),var(--tw-bg-opacity))}.bg-primary-300{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-300-rgb),var(--tw-bg-opacity))}.bg-primary-400{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-400-rgb),var(--tw-bg-opacity))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-500-rgb),var(--tw-bg-opacity))}.bg-primary-600{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-600-rgb),var(--tw-bg-opacity))}.bg-primary-700{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-700-rgb),var(--tw-bg-opacity))}.bg-primary-800{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-800-rgb),var(--tw-bg-opacity))}.bg-primary-900{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-900-rgb),var(--tw-bg-opacity))}.bg-primary-950{--tw-bg-opacity:1;background-color:rgba(var(--color-primary-950-rgb),var(--tw-bg-opacity))}.bg-secondary-50{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-50-rgb),var(--tw-bg-opacity))}.bg-secondary-100{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-100-rgb),var(--tw-bg-opacity))}.bg-secondary-200{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-200-rgb),var(--tw-bg-opacity))}.bg-secondary-300{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-300-rgb),var(--tw-bg-opacity))}.bg-secondary-400{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-400-rgb),var(--tw-bg-opacity))}.bg-secondary-500{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-500-rgb),var(--tw-bg-opacity))}.bg-secondary-600{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-600-rgb),var(--tw-bg-opacity))}.bg-secondary-700{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-700-rgb),var(--tw-bg-opacity))}.bg-secondary-800{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-800-rgb),var(--tw-bg-opacity))}.bg-secondary-900{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-900-rgb),var(--tw-bg-opacity))}.bg-secondary-950{--tw-bg-opacity:1;background-color:rgba(var(--color-secondary-950-rgb),var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-50-rgb),var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-100-rgb),var(--tw-bg-opacity))}.bg-gray-200,.bg-lightest{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-200-rgb),var(--tw-bg-opacity))}.bg-gray-300,.bg-lighter{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-300-rgb),var(--tw-bg-opacity))}.bg-gray-400,.bg-light{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-400-rgb),var(--tw-bg-opacity))}.bg-gray,.bg-gray-500{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-500-rgb),var(--tw-bg-opacity))}.bg-darken,.bg-gray-600{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-600-rgb),var(--tw-bg-opacity))}.bg-darker,.bg-gray-700{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-700-rgb),var(--tw-bg-opacity))}.bg-darkest,.bg-gray-800{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-800-rgb),var(--tw-bg-opacity))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-900-rgb),var(--tw-bg-opacity))}.bg-gray-950{--tw-bg-opacity:1;background-color:rgba(var(--color-gray-950-rgb),var(--tw-bg-opacity))}.bg-canvas{--tw-bg-opacity:1;background-color:rgba(var(--color-canvas-rgb),var(--tw-bg-opacity))}.bg-surface-light{--tw-bg-opacity:1;background-color:rgba(var(--color-surface-light-rgb),var(--tw-bg-opacity))}.bg-surface{--tw-bg-opacity:1;background-color:rgba(var(--color-surface-rgb),var(--tw-bg-opacity))}.bg-surface-strong{--tw-bg-opacity:1;background-color:rgba(var(--color-surface-strong-rgb),var(--tw-bg-opacity))}.bg-inverse{--tw-bg-opacity:1;background-color:rgba(var(--color-inverse-rgb),var(--tw-bg-opacity))}.bg-fore-in-light{--tw-bg-opacity:1;background-color:rgba(var(--color-fore-in-light-rgb),var(--tw-bg-opacity))}.bg-fore-in-dark{--tw-bg-opacity:1;background-color:rgba(var(--color-fore-in-dark-rgb),var(--tw-bg-opacity))}.bg-fore{--tw-bg-opacity:1;background-color:rgba(var(--color-fore-rgb),var(--tw-bg-opacity))}.bg-focus{--tw-bg-opacity:1;background-color:rgba(var(--color-focus-rgb),var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgba(var(--color-white-rgb),var(--tw-bg-opacity))}.bg-black{--tw-bg-opacity:1;background-color:rgba(var(--color-black-rgb),var(--tw-bg-opacity))}.bg-transparent{background-color:transparent}.bg-current{background-color:currentColor}.bg-inherit{background-color:inherit}.bg-none{background-image:none}.bg-opacity-0{--tw-bg-opacity:0}.bg-opacity-5{--tw-bg-opacity:.05}.bg-opacity-10{--tw-bg-opacity:.1}.bg-opacity-20{--tw-bg-opacity:.2}.bg-opacity-25{--tw-bg-opacity:.25}.bg-opacity-30{--tw-bg-opacity:.3}.bg-opacity-40{--tw-bg-opacity:.4}.bg-opacity-50{--tw-bg-opacity:.5}.bg-opacity-60{--tw-bg-opacity:.6}.bg-opacity-70{--tw-bg-opacity:.7}.bg-opacity-80{--tw-bg-opacity:.8}.bg-opacity-90{--tw-bg-opacity:.9}.bg-opacity-100{--tw-bg-opacity:1}.bg-auto{background-size:auto}.bg-cover{background-size:cover}.bg-contain{background-size:contain}.bg-repeat{background-repeat:repeat}.bg-no-repeat{background-repeat:no-repeat}.bg-repeat-x{background-repeat:repeat-x}.bg-repeat-y{background-repeat:repeat-y}.bg-repeat-round{background-repeat:round}.bg-repeat-space{background-repeat:space}.bg-bottom{background-position:bottom}.bg-center{background-position:50%}.bg-left{background-position:0}.bg-left-bottom{background-position:0 100%}.bg-left-top{background-position:0 0}.bg-right{background-position:100%}.bg-right-bottom{background-position:100% 100%}.bg-right-top{background-position:100% 0}.bg-top{background-position:top}.bg-fixed{background-attachment:fixed}.bg-local{background-attachment:local}.bg-scroll{background-attachment:scroll}.divider{align-self:stretch;border-color:currentColor;border-left-width:1px;border-top-width:1px;opacity:.1}.border-0{border-width:0}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-t{border-top-width:1px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-b{border-bottom-width:1px}.border-t-0{border-top-width:0}.border-r-0{border-right-width:0}.border-b-0{border-bottom-width:0}.border-l-0{border-left-width:0}.border-t-2{border-top-width:2px}.border-l-2{border-left-width:2px}.border-r-2{border-right-width:2px}.border-b-2{border-bottom-width:2px}.border-t-4{border-top-width:4px}.border-l-4{border-left-width:4px}.border-r-4{border-right-width:4px}.border-b-4{border-bottom-width:4px}.border-light{--tw-border-opacity:1;border-color:rgba(var(--color-border-light-rgb),var(--tw-border-opacity))}.border-strong{--tw-border-opacity:1;border-color:rgba(var(--color-border-strong-rgb),var(--tw-border-opacity))}.border-primary{--tw-border-opacity:1;border-color:rgba(var(--color-primary-500-rgb),var(--tw-border-opacity))}.border-secondary{--tw-border-opacity:1;border-color:rgba(var(--color-secondary-500-rgb),var(--tw-border-opacity))}.border-success{--tw-border-opacity:1;border-color:rgba(var(--color-success-500-rgb),var(--tw-border-opacity))}.border-warning{--tw-border-opacity:1;border-color:rgba(var(--color-warning-500-rgb),var(--tw-border-opacity))}.border-danger{--tw-border-opacity:1;border-color:rgba(var(--color-danger-500-rgb),var(--tw-border-opacity))}.border-important{--tw-border-opacity:1;border-color:rgba(var(--color-important-500-rgb),var(--tw-border-opacity))}.border-special{--tw-border-opacity:1;border-color:rgba(var(--color-special-500-rgb),var(--tw-border-opacity))}.border-gray-50{--tw-border-opacity:1;border-color:rgba(var(--color-gray-50-rgb),var(--tw-border-opacity))}.border-gray-100{--tw-border-opacity:1;border-color:rgba(var(--color-gray-100-rgb),var(--tw-border-opacity))}.border-gray-200{--tw-border-opacity:1;border-color:rgba(var(--color-gray-200-rgb),var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgba(var(--color-gray-300-rgb),var(--tw-border-opacity))}.border-gray-400{--tw-border-opacity:1;border-color:rgba(var(--color-gray-400-rgb),var(--tw-border-opacity))}.border-gray,.border-gray-500{--tw-border-opacity:1;border-color:rgba(var(--color-gray-500-rgb),var(--tw-border-opacity))}.border-gray-600{--tw-border-opacity:1;border-color:rgba(var(--color-gray-600-rgb),var(--tw-border-opacity))}.border-gray-700{--tw-border-opacity:1;border-color:rgba(var(--color-gray-700-rgb),var(--tw-border-opacity))}.border-gray-800{--tw-border-opacity:1;border-color:rgba(var(--color-gray-800-rgb),var(--tw-border-opacity))}.border-gray-900{--tw-border-opacity:1;border-color:rgba(var(--color-gray-900-rgb),var(--tw-border-opacity))}.border-gray-950{--tw-border-opacity:1;border-color:rgba(var(--color-gray-950-rgb),var(--tw-border-opacity))}.border-white{--tw-border-opacity:1;border-color:rgba(var(--color-white-rgb),var(--tw-border-opacity))}.border-black{--tw-border-opacity:1;border-color:rgba(var(--color-black-rgb),var(--tw-border-opacity))}.border-canvas{--tw-border-opacity:1;border-color:rgba(var(--color-canvas-rgb),var(--tw-border-opacity))}.border-surface-light{--tw-border-opacity:1;border-color:rgba(var(--color-surface-light-rgb),var(--tw-border-opacity))}.border-surface{--tw-border-opacity:1;border-color:rgba(var(--color-surface-rgb),var(--tw-border-opacity))}.border-surface-strong{--tw-border-opacity:1;border-color:rgba(var(--color-surface-strong-rgb),var(--tw-border-opacity))}.border-inverse{--tw-border-opacity:1;border-color:rgba(var(--color-inverse-rgb),var(--tw-border-opacity))}.border-fore-in-light{--tw-border-opacity:1;border-color:rgba(var(--color-fore-in-light-rgb),var(--tw-border-opacity))}.border-fore-in-dark{--tw-border-opacity:1;border-color:rgba(var(--color-fore-in-dark-rgb),var(--tw-border-opacity))}.border-fore{--tw-border-opacity:1;border-color:rgba(var(--color-fore-rgb),var(--tw-border-opacity))}.border-focus{--tw-border-opacity:1;border-color:rgba(var(--color-focus-rgb),var(--tw-border-opacity))}.border-link{--tw-border-opacity:1;border-color:rgba(var(--color-link-rgb),var(--tw-border-opacity))}.border-link-hover{--tw-border-opacity:1;border-color:rgba(var(--color-link-hover-rgb),var(--tw-border-opacity))}.border-transparent{border-color:transparent}.border-current{border-color:currentColor}.border-inherit{border-color:inherit}.border-solid{border-style:solid}.border-dashed{border-style:dashed}.border-dotted{border-style:dotted}.border-double{border-style:double}.border-hidden{border-style:hidden}.border-none{border-style:none}.border-opacity-0{--tw-border-opacity:0}.border-opacity-5{--tw-border-opacity:.05}.border-opacity-10{--tw-border-opacity:.1}.border-opacity-20{--tw-border-opacity:.2}.border-opacity-25{--tw-border-opacity:.25}.border-opacity-30{--tw-border-opacity:.3}.border-opacity-40{--tw-border-opacity:.4}.border-opacity-50{--tw-border-opacity:.5}.border-opacity-60{--tw-border-opacity:.6}.border-opacity-70{--tw-border-opacity:.7}.border-opacity-80{--tw-border-opacity:.8}.border-opacity-90{--tw-border-opacity:.9}.border-opacity-95{--tw-border-opacity:.95}.border-opacity-100{--tw-border-opacity:1}.rounded-none{border-radius:var(--radius-none)}.rounded-sm{border-radius:var(--radius-sm)}.rounded{border-radius:var(--radius)}.rounded-md{border-radius:var(--radius-md)}.rounded-lg{border-radius:var(--radius-lg)}.rounded-xl{border-radius:var(--radius-xl)}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-3xl{border-radius:var(--radius-3xl)}.circle,.rounded-full{border-radius:var(--radius-full)}.rounded-l-none{border-bottom-left-radius:var(--radius-none);border-top-left-radius:var(--radius-none)}.rounded-t-none{border-top-left-radius:var(--radius-none)}.rounded-r-none,.rounded-t-none{border-top-right-radius:var(--radius-none)}.rounded-b-none,.rounded-r-none{border-bottom-right-radius:var(--radius-none)}.rounded-b-none{border-bottom-left-radius:var(--radius-none)}.rounded-br-none{border-bottom-right-radius:var(--radius-none)}.rounded-bl-none{border-bottom-left-radius:var(--radius-none)}.rounded-tr-none{border-top-right-radius:var(--radius-none)}.rounded-tl-none{border-top-left-radius:var(--radius-none)}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000);--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-border-rgb),var(--tw-ring-opacity))}.ring-border-light{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-border-light-rgb),var(--tw-ring-opacity))}.ring-border{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-border-rgb),var(--tw-ring-opacity))}.ring-border-strong{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-border-strong-rgb),var(--tw-ring-opacity))}.ring-light{--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.05}.ring-gray{--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.1}.ring-dark{--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.2}.ring-darker{--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.3}.ring-darkest{--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity));--tw-ring-opacity:0.5}.ring-primary{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-primary-500-rgb),var(--tw-ring-opacity))}.ring-secondary{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-secondary-500-rgb),var(--tw-ring-opacity))}.ring-warning{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-warning-500-rgb),var(--tw-ring-opacity))}.ring-success{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-success-500-rgb),var(--tw-ring-opacity))}.ring-danger{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-danger-500-rgb),var(--tw-ring-opacity))}.ring-important{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-important-500-rgb),var(--tw-ring-opacity))}.ring-special{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-special-500-rgb),var(--tw-ring-opacity))}.ring-gray-50{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-50-rgb),var(--tw-ring-opacity))}.ring-gray-100{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-100-rgb),var(--tw-ring-opacity))}.ring-gray-200{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-200-rgb),var(--tw-ring-opacity))}.ring-gray-300{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-300-rgb),var(--tw-ring-opacity))}.ring-gray-400{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-400-rgb),var(--tw-ring-opacity))}.ring-gray-500{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-500-rgb),var(--tw-ring-opacity))}.ring-gray-600{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-600-rgb),var(--tw-ring-opacity))}.ring-gray-700{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-700-rgb),var(--tw-ring-opacity))}.ring-gray-800{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-800-rgb),var(--tw-ring-opacity))}.ring-gray-900{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-900-rgb),var(--tw-ring-opacity))}.ring-gray-950{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-gray-950-rgb),var(--tw-ring-opacity))}.ring-inverse{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-inverse-rgb),var(--tw-ring-opacity))}.ring-canvas{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-canvas-rgb),var(--tw-ring-opacity))}.ring-surface{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-surface-rgb),var(--tw-ring-opacity))}.ring-fore{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-fore-rgb),var(--tw-ring-opacity))}.ring-transparent{--tw-ring-color:transparent}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-black-rgb),var(--tw-ring-opacity))}.ring-white{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--color-white-rgb),var(--tw-ring-opacity))}.ring-inherit{--tw-ring-color:inherit}.ring-current{--tw-ring-color:currentColor}.ring-opacity-0{--tw-ring-opacity:0}.ring-opacity-5{--tw-ring-opacity:.05}.ring-opacity-10{--tw-ring-opacity:.1}.ring-opacity-20{--tw-ring-opacity:.2}.ring-opacity-30{--tw-ring-opacity:.3}.ring-opacity-40{--tw-ring-opacity:.4}.ring-opacity-50{--tw-ring-opacity:.5}.ring-opacity-60{--tw-ring-opacity:.6}.ring-opacity-70{--tw-ring-opacity:.7}.ring-opacity-80{--tw-ring-opacity:.8}.ring-opacity-90{--tw-ring-opacity:.9}.ring-opacity-100{--tw-ring-opacity:1}.ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-0,.ring-2{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-3{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-3,.ring-4{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-4{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-8{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(8px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-inset{--tw-ring-inset:inset}.ring-offset-0{--tw-ring-offset-width:0px}.ring-offset-1{--tw-ring-offset-width:1px}.ring-offset-2{--tw-ring-offset-width:2px}.ring-offset-4{--tw-ring-offset-width:4px}.ring-offset-8{--tw-ring-offset-width:8px}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.04),0 0 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color),0 0 2px -1px var(--tw-shadow-color)}.shadow,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow{--tw-shadow:0 3px 8px 0 rgba(0,0,0,.04),0 1px 2px -1px rgba(0,0,0,.01);--tw-shadow-colored:0 3px 8px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 2px 6px -1px rgba(0,0,0,.2),0 1px 4px -2px rgba(0,0,0,.15);--tw-shadow-colored:0 2px 6px -1px var(--tw-shadow-color),0 1px 4px -2px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 5px 15px -3px rgba(0,0,0,.2),0 2px 6px -4px rgba(0,0,0,.15);--tw-shadow-colored:0 5px 15px -3px var(--tw-shadow-color),0 2px 6px -4px var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 10px 25px -5px rgba(0,0,0,.2),0 4px 10px -6px rgba(0,0,0,.15);--tw-shadow-colored:0 10px 25px -5px var(--tw-shadow-color),0 4px 10px -6px var(--tw-shadow-color)}.shadow-2xl,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 10px 50px -10px rgba(0,0,0,.4),0 8px 20px -10px rgba(0,0,0,.3);--tw-shadow-colored:0 10px 50px -10px var(--tw-shadow-color),0 8px 20px -10px var(--tw-shadow-color)}.shadow-inner{--tw-shadow:inset 0 2px 4px 0 rgba(0,0,0,.05);--tw-shadow-colored:inset 0 2px 4px 0 var(--tw-shadow-color)}.shadow-inner,.shadow-none{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-none{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}.muted{opacity:var(--opacity-muted)}.opacity-0{opacity:0}.opacity-5{opacity:.05}.opacity-10{opacity:.1}.opacity-20{opacity:.2}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-40{opacity:.4}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.opacity-95{opacity:.95}.opacity-100{opacity:1}.blur-none{--tw-blur:blur(0)}.blur-none,.blur-sm{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-sm{--tw-blur:blur(4px)}.blur{--tw-blur:blur(8px)}.blur,.blur-md{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-md{--tw-blur:blur(12px)}.blur-lg{--tw-blur:blur(16px)}.blur-lg,.blur-xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-xl{--tw-blur:blur(24px)}.grayscale{--tw-grayscale:grayscale(100%)}.grayscale,.invert{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.invert{--tw-invert:invert(100%)}.filter-none{filter:none!important}.drop-shadow-none{--tw-drop-shadow:drop-shadow(0 0 #0000)}.drop-shadow-none,.drop-shadow-sm{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow-sm{--tw-drop-shadow:drop-shadow(0 1px 1px rgba(0,0,0,.05))}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px rgba(0,0,0,.1)) drop-shadow(0 1px 1px rgba(0,0,0,.06))}.drop-shadow,.drop-shadow-md{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow-md{--tw-drop-shadow:drop-shadow(0 4px 3px rgba(0,0,0,.07)) drop-shadow(0 2px 2px rgba(0,0,0,.06))}.drop-shadow-lg{--tw-drop-shadow:drop-shadow(0 10px 8px rgba(0,0,0,.04)) drop-shadow(0 4px 3px rgba(0,0,0,.1))}.drop-shadow-lg,.drop-shadow-xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow-xl{--tw-drop-shadow:drop-shadow(0 20px 13px rgba(0,0,0,.03)) drop-shadow(0 8px 5px rgba(0,0,0,.08))}.drop-shadow-2xl{--tw-drop-shadow:drop-shadow(0 25px 25px rgba(0,0,0,.15));filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-none{--tw-backdrop-blur:blur(0)}.backdrop-blur-none,.backdrop-blur-sm{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.backdrop-blur{--tw-backdrop-blur:blur(8px)}.backdrop-blur,.backdrop-blur-md{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-lg{--tw-backdrop-blur:blur(16px)}.backdrop-blur-lg,.backdrop-blur-xl{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur:blur(24px)}.backdrop-blur-2xl{--tw-backdrop-blur:blur(40px)}.backdrop-blur-2xl,.backdrop-blur-3xl{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-3xl{--tw-backdrop-blur:blur(64px)}.transition{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-duration:.15s;transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-duration:.15s;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-duration:.15s;transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-shadow{transition-duration:.15s;transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-duration:.15s;transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-none{transition-property:none}.fade,.fade-from-bottom,.fade-from-center,.fade-from-left,.fade-from-right,.fade-from-top{opacity:0;transition-duration:.15s;transition-property:opacity,transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.fade-from-bottom,.fade-from-center,.fade-from-left,.fade-from-right,.fade-from-top{--tw-scale-x:.9;--tw-scale-y:.9}.fade-from-bottom,.fade-from-center,.fade-from-left,.fade-from-right,.fade-from-top{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.fade-from-bottom{--tw-translate-y:25%}.fade-from-top{--tw-translate-y:-25%}.fade-from-left,.fade-from-top{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.fade-from-left{--tw-translate-x:-25%}.fade-from-right{--tw-translate-x:25%}.fade-from-bottom.in,.fade-from-center.in,.fade-from-left.in,.fade-from-right,.fade-from-right.in,.fade-from-top.in,.fade.in{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.fade-from-bottom.in,.fade-from-center.in,.fade-from-left.in,.fade-from-right.in,.fade-from-top.in,.fade.in{--tw-translate-x:0px;--tw-translate-y:0px;--tw-scale-x:1;--tw-scale-y:1;opacity:1}.duration-75{transition-duration:75ms}.duration-100{transition-duration:.1s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.duration-1000{transition-duration:1s}@keyframes -spin{to{transform:rotate(1turn)}}.animate-spin,.spin{animation:-spin 1s linear infinite}@keyframes -ping{75%,to{opacity:0;transform:scale(2)}}.animate-ping,.ping{animation:-ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes -pulse{50%{opacity:.5}}.animate-pulse,.pulse{animation:-pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes -bounce{0%,to{animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}.animate-bounce,.bounce{animation:-bounce 1s infinite}.scale-0{--tw-scale-x:0;--tw-scale-y:0}.scale-0,.scale-50{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-50{--tw-scale-x:.5;--tw-scale-y:.5}.scale-75{--tw-scale-x:.75;--tw-scale-y:.75}.scale-75,.scale-90{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-90{--tw-scale-x:.9;--tw-scale-y:.9}.scale-95{--tw-scale-x:.95;--tw-scale-y:.95}.scale-100,.scale-95{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05}.scale-105,.scale-110{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1}.scale-125{--tw-scale-x:1.25;--tw-scale-y:1.25}.scale-125,.scale-150{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-150{--tw-scale-x:1.5;--tw-scale-y:1.5}.flip-x{--tw-scale-x:-1}.flip-x,.flip-y{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.flip-y{--tw-scale-y:-1}.rotate-0{--tw-rotate:0deg}.rotate-0,.rotate-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-1{--tw-rotate:1deg}.rotate-2{--tw-rotate:2deg}.rotate-2,.rotate-3{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-3{--tw-rotate:3deg}.rotate-6{--tw-rotate:6deg}.rotate-12,.rotate-6{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-12{--tw-rotate:12deg}.rotate-45{--tw-rotate:45deg}.rotate-45,.rotate-90{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-90{--tw-rotate:90deg}.rotate-135{--tw-rotate:135deg}.rotate-135,.rotate-180{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate:180deg}.rotate-270{--tw-rotate:270deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.basis-0{flex-basis:0px}.basis-1{flex-basis:0.25rem}.basis-2{flex-basis:0.5rem}.basis-3{flex-basis:0.75rem}.basis-4{flex-basis:1rem}.basis-5{flex-basis:1.25rem}.basis-6{flex-basis:1.5rem}.basis-7{flex-basis:1.75rem}.basis-8{flex-basis:2rem}.basis-9{flex-basis:2.25rem}.basis-10{flex-basis:2.5rem}.basis-11{flex-basis:2.75rem}.basis-12{flex-basis:3rem}.basis-14{flex-basis:3.5rem}.basis-16{flex-basis:4rem}.basis-20{flex-basis:5rem}.basis-24{flex-basis:6rem}.basis-28{flex-basis:7rem}.basis-32{flex-basis:8rem}.basis-36{flex-basis:9rem}.basis-40{flex-basis:10rem}.basis-44{flex-basis:11rem}.basis-48{flex-basis:12rem}.basis-52{flex-basis:13rem}.basis-56{flex-basis:14rem}.basis-60{flex-basis:15rem}.basis-64{flex-basis:16rem}.basis-72{flex-basis:18rem}.basis-80{flex-basis:20rem}.basis-96{flex-basis:24rem}.basis-auto{flex-basis:auto}.basis-px{flex-basis:1px}.basis-0\.5{flex-basis:0.125rem}.basis-1\.5{flex-basis:0.375rem}.basis-2\.5{flex-basis:0.625rem}.basis-3\.5{flex-basis:0.875rem}.basis-1\/2{flex-basis:50%}.basis-1\/3{flex-basis:33.333333%}.basis-2\/3{flex-basis:66.666667%}.basis-1\/4{flex-basis:25%}.basis-2\/4{flex-basis:50%}.basis-3\/4{flex-basis:75%}.basis-1\/5{flex-basis:20%}.basis-2\/5{flex-basis:40%}.basis-3\/5{flex-basis:60%}.basis-4\/5{flex-basis:80%}.basis-1\/6{flex-basis:16.666667%}.basis-2\/6{flex-basis:33.333333%}.basis-3\/6{flex-basis:50%}.basis-4\/6{flex-basis:66.666667%}.basis-5\/6{flex-basis:83.333333%}.basis-1\/12{flex-basis:8.333333%}.basis-2\/12{flex-basis:16.666667%}.basis-3\/12{flex-basis:25%}.basis-4\/12{flex-basis:33.333333%}.basis-5\/12{flex-basis:41.666667%}.basis-6\/12{flex-basis:50%}.basis-7\/12{flex-basis:58.333333%}.basis-8\/12{flex-basis:66.666667%}.basis-9\/12{flex-basis:75%}.basis-10\/12{flex-basis:83.333333%}.basis-11\/12{flex-basis:91.666667%}.basis-full{flex-basis:100%}.row{display:flex;flex-direction:row}.center,.col{display:flex;flex-direction:column}.center,.center-row{align-items:center;justify-content:center}.center-row{display:flex;flex-direction:row}.center-x,.center-y{align-items:center;display:flex}.center-y{flex-direction:column}.row-reverse{display:flex;flex-direction:row-reverse}.col-reverse{display:flex;flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.flex-wrap-reverse{flex-wrap:wrap-reverse}.flex-nowrap{flex-wrap:nowrap}.flex-1{flex:1 1 0%}.flex-auto{flex:1 1 auto}.flex-initial{flex:0 1 auto}.flex-none{flex:none}.grow{flex-grow:1!important}.grow-0{flex-grow:0!important}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-9{order:9}.order-10{order:10}.order-11{order:11}.order-12{order:12}.order-first{order:-9999}.order-last{order:9999}.order-none{order:0}.gap-0{gap:0}.gap-x-0{-moz-column-gap:0;column-gap:0}.gap-y-0{row-gap:0}.gap-px{gap:1px}.gap-x-px{-moz-column-gap:1px;column-gap:1px}.gap-y-px{row-gap:1px}.gap-0\.5{gap:.125rem}.gap-x-0\.5{-moz-column-gap:.125rem;column-gap:.125rem}.gap-y-0\.5{row-gap:.125rem}.gap-3px{gap:3px}.gap-1{gap:.25rem}.gap-x-1{-moz-column-gap:.25rem;column-gap:.25rem}.gap-y-1{row-gap:.25rem}.gap-1\.5{gap:.375rem}.gap-x-1\.5{-moz-column-gap:.375rem;column-gap:.375rem}.gap-y-1\.5{row-gap:.375rem}.gap-2{gap:.5rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-y-2{row-gap:.5rem}.gap-2\.5{gap:.625rem}.gap-x-2\.5{-moz-column-gap:.625rem;column-gap:.625rem}.gap-y-2\.5{row-gap:.625rem}.gap-3{gap:.75rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.gap-y-3{row-gap:.75rem}.gap-3\.5{gap:.875rem}.gap-x-3\.5{-moz-column-gap:.875rem;column-gap:.875rem}.gap-y-3\.5{row-gap:.875rem}.gap-4{gap:1rem}.gap-x-4{-moz-column-gap:1rem;column-gap:1rem}.gap-y-4{row-gap:1rem}.gap-5{gap:1.25rem}.gap-x-5{-moz-column-gap:1.25rem;column-gap:1.25rem}.gap-y-5{row-gap:1.25rem}.gap-6{gap:1.5rem}.gap-x-6{-moz-column-gap:1.5rem;column-gap:1.5rem}.gap-y-6{row-gap:1.5rem}.gap-7{gap:1.75rem}.gap-x-7{-moz-column-gap:1.75rem;column-gap:1.75rem}.gap-y-7{row-gap:1.75rem}.gap-8{gap:2rem}.gap-x-8{-moz-column-gap:2rem;column-gap:2rem}.gap-y-8{row-gap:2rem}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.justify-evenly{justify-content:space-evenly}.self-auto{align-self:auto}.self-start{align-self:flex-start}.self-end{align-self:flex-end}.self-center{align-self:center}.self-stretch{align-self:stretch}.self-baseline{align-self:baseline}.content-center{align-content:center}.content-start{align-content:flex-start}.content-end{align-content:flex-end}.content-between{align-content:space-between}.content-around{align-content:space-around}.content-evenly{align-content:space-evenly}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}@font-face{font-display:fallback;font-family:D-DIN-PRO;font-weight:400;src:url(./fonts/D-DIN-PRO-400-Regular.otf)}@font-face{font-display:fallback;font-family:D-DIN-PRO;font-weight:500;src:url(./fonts/D-DIN-PRO-700-Bold.otf)}.num{font-family:D-DIN-PRO}@font-face{font-display:fallback;font-family:Nunito;font-weight:400;src:url(./fonts/Nunito-Regular.ttf)}@font-face{font-display:fallback;font-family:Nunito;font-weight:700;src:url(./fonts/Nunito-bold.ttf)}@font-face{font-display:fallback;font-family:Nunito;font-weight:300;src:url(./fonts/Nunito-Light.ttf)}.font-brand{font-family:Nunito;font-weight:400}@supports not (gap:4px){.gap-px>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1px*(1 - var(--tw-space-x-reverse)));margin-right:calc(1px*var(--tw-space-x-reverse))}.gap-0\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2px*(1 - var(--tw-space-x-reverse)));margin-right:calc(2px*var(--tw-space-x-reverse))}.gap-3px>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(3px*(1 - var(--tw-space-x-reverse)));margin-right:calc(3px*var(--tw-space-x-reverse))}.gap-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}.gap-1\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(6px*(1 - var(--tw-space-x-reverse)));margin-right:calc(6px*var(--tw-space-x-reverse))}.gap-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.5rem*var(--tw-space-x-reverse))}.gap-2\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(10px*(1 - var(--tw-space-x-reverse)));margin-right:calc(10px*var(--tw-space-x-reverse))}.gap-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.75rem*var(--tw-space-x-reverse))}.gap-3\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(14px*(1 - var(--tw-space-x-reverse)));margin-right:calc(14px*var(--tw-space-x-reverse))}.gap-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1rem*var(--tw-space-x-reverse))}.gap-5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.25rem*var(--tw-space-x-reverse))}.gap-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.5rem*var(--tw-space-x-reverse))}.gap-7>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(1.75rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(1.75rem*var(--tw-space-x-reverse))}.gap-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(2rem*var(--tw-space-x-reverse))}.col.gap-px>:not([hidden])~:not([hidden]),.gap-px.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1px*var(--tw-space-y-reverse));margin-top:calc(1px*(1 - var(--tw-space-y-reverse)))}.col.gap-0\.5>:not([hidden])~:not([hidden]),.gap-0\.5.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(2px*var(--tw-space-y-reverse));margin-top:calc(2px*(1 - var(--tw-space-y-reverse)))}.col.gap-3px>:not([hidden])~:not([hidden]),.gap-3px.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(3px*var(--tw-space-y-reverse));margin-top:calc(3px*(1 - var(--tw-space-y-reverse)))}.col.gap-1>:not([hidden])~:not([hidden]),.gap-1.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.25rem*var(--tw-space-y-reverse));margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)))}.col.gap-1\.5>:not([hidden])~:not([hidden]),.gap-1\.5.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(6px*var(--tw-space-y-reverse));margin-top:calc(6px*(1 - var(--tw-space-y-reverse)))}.col.gap-2>:not([hidden])~:not([hidden]),.gap-2.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.5rem*var(--tw-space-y-reverse));margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)))}.col.gap-2\.5>:not([hidden])~:not([hidden]),.gap-2\.5.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(10px*var(--tw-space-y-reverse));margin-top:calc(10px*(1 - var(--tw-space-y-reverse)))}.col.gap-3>:not([hidden])~:not([hidden]),.gap-3.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(.75rem*var(--tw-space-y-reverse));margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)))}.col.gap-3\.5>:not([hidden])~:not([hidden]),.gap-3\.5.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(14px*var(--tw-space-y-reverse));margin-top:calc(14px*(1 - var(--tw-space-y-reverse)))}.col.gap-4>:not([hidden])~:not([hidden]),.gap-4.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1rem*var(--tw-space-y-reverse));margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)))}.col.gap-5>:not([hidden])~:not([hidden]),.gap-5.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.25rem*var(--tw-space-y-reverse));margin-top:calc(1.25rem*(1 - var(--tw-space-y-reverse)))}.col.gap-6>:not([hidden])~:not([hidden]),.gap-6.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.5rem*var(--tw-space-y-reverse));margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)))}.col.gap-7>:not([hidden])~:not([hidden]),.gap-7.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(1.75rem*var(--tw-space-y-reverse));margin-top:calc(1.75rem*(1 - var(--tw-space-y-reverse)))}.col.gap-8>:not([hidden])~:not([hidden]),.gap-8.flex-row>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-bottom:calc(2rem*var(--tw-space-y-reverse));margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)))}.nav-item>a>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)));margin-right:calc(.25rem*var(--tw-space-x-reverse))}} \ No newline at end of file diff --git a/www/js/zui3/zui.zentao.js b/www/js/zui3/zui.zentao.js index 81ce707fcc..8f5b590dc5 100644 --- a/www/js/zui3/zui.zentao.js +++ b/www/js/zui3/zui.zentao.js @@ -1,7 +1,7 @@ -var dh=(D,Yt,be)=>{if(!Yt.has(D))throw TypeError("Cannot "+be)};var ye=(D,Yt,be)=>(dh(D,Yt,"read from private field"),be?be.call(D):Yt.get(D)),Te=(D,Yt,be)=>{if(Yt.has(D))throw TypeError("Cannot add the same private member more than once");Yt instanceof WeakSet?Yt.add(D):Yt.set(D,be)},De=(D,Yt,be,ea)=>(dh(D,Yt,"write to private field"),ea?ea.call(D,be):Yt.set(D,be),be);var fh=(D,Yt,be)=>(dh(D,Yt,"access private method"),be);(function(D,Yt){typeof exports=="object"&&typeof module<"u"?Yt(exports):typeof define=="function"&&define.amd?define(["exports"],Yt):(D=typeof globalThis<"u"?globalThis:D||self,Yt(D.zui={}))})(this,function(D){var xo,So,Ai,Ii,Di,Ri,Li,Oi,$o,gp,To,yp,Mn,An;"use strict";const Yt="",be="",ea="",m_="",g_="",y_="",b_="",__="",v_="",w_="",C_="",k_="",vp="3.0.0",Xe=document,Ji=window,ph=Xe.documentElement,Hn=Xe.createElement.bind(Xe),mh=Hn("div"),na=Hn("table"),wp=Hn("tbody"),gh=Hn("tr"),{isArray:tr,prototype:yh}=Array,{concat:Cp,filter:sa,indexOf:bh,map:_h,push:kp,slice:vh,some:ia,splice:xp}=yh,Sp=/^#(?:[\w-]|\\.|[^\x00-\xa0])*$/,$p=/^\.(?:[\w-]|\\.|[^\x00-\xa0])*$/,Tp=/<.+>/,Np=/^\w+$/;function ra(s,t){const e=Ep(t);return!s||!e&&!Wn(t)&&!Xt(t)?[]:!e&&$p.test(s)?t.getElementsByClassName(s.slice(1).replace(/\\/g,"")):!e&&Np.test(s)?t.getElementsByTagName(s):t.querySelectorAll(s)}class er{constructor(t,e){if(!t)return;if(oa(t))return t;let n=t;if(ce(t)){const i=e||Xe;if(n=Sp.test(t)&&Wn(i)?i.getElementById(t.slice(1).replace(/\\/g,"")):Tp.test(t)?$h(t):oa(i)?i.find(t):ce(i)?g(i).find(t):ra(t,i),!n)return}else if(Kn(t))return this.ready(t);(n.nodeType||n===Ji)&&(n=[n]),this.length=n.length;for(let i=0,r=this.length;i{for(;t.firstChild;)t.removeChild(t.firstChild)})};function nr(...s){const t=Mp(s[0])?s.shift():!1,e=s.shift(),n=s.length;if(!e)return{};if(!n)return nr(t,g,e);for(let i=0;i{Xt(r)&&ne(e,(o,a)=>{n?t?r.classList.add(a):r.classList.remove(a):r.classList.toggle(a)})})},it.addClass=function(s){return this.toggleClass(s,!0)},it.removeAttr=function(s){const t=sr(s);return this.each((e,n)=>{Xt(n)&&ne(t,(i,r)=>{n.removeAttribute(r)})})};function Ip(s,t){if(s){if(ce(s)){if(arguments.length<2){if(!this[0]||!Xt(this[0]))return;const e=this[0].getAttribute(s);return Qs(e)?void 0:e}return _e(t)?this:Qs(t)?this.removeAttr(s):this.each((e,n)=>{Xt(n)&&n.setAttribute(s,t)})}for(const e in s)this.attr(e,s[e]);return this}}it.attr=Ip,it.removeClass=function(s){return arguments.length?this.toggleClass(s,!1):this.attr("class","")},it.hasClass=function(s){return!!s&&ia.call(this,t=>Xt(t)&&t.classList.contains(s))},it.get=function(s){return _e(s)?vh.call(this):(s=Number(s),this[s<0?s+this.length:s])},it.eq=function(s){return g(this.get(s))},it.first=function(){return this.eq(0)},it.last=function(){return this.eq(-1)};function Dp(s){return _e(s)?this.get().map(t=>Xt(t)||Pp(t)?t.textContent:"").join(""):this.each((t,e)=>{Xt(e)&&(e.textContent=s)})}it.text=Dp;function Ze(s,t,e){if(!Xt(s))return;const n=Ji.getComputedStyle(s,null);return e?n.getPropertyValue(t)||void 0:n[t]||s.style[t]}function je(s,t){return parseInt(Ze(s,t),10)||0}function Ch(s,t){return je(s,`border${t?"Left":"Top"}Width`)+je(s,`padding${t?"Left":"Top"}`)+je(s,`padding${t?"Right":"Bottom"}`)+je(s,`border${t?"Right":"Bottom"}Width`)}const la={};function Rp(s){if(la[s])return la[s];const t=Hn(s);Xe.body.insertBefore(t,null);const e=Ze(t,"display");return Xe.body.removeChild(t),la[s]=e!=="none"?e:"block"}function kh(s){return Ze(s,"display")==="none"}function xh(s,t){const e=s&&(s.matches||s.webkitMatchesSelector||s.msMatchesSelector);return!!e&&!!t&&e.call(s,t)}function ir(s){return ce(s)?(t,e)=>xh(e,s):Kn(s)?s:oa(s)?(t,e)=>s.is(e):s?(t,e)=>e===s:()=>!1}it.filter=function(s){const t=ir(s);return g(sa.call(this,(e,n)=>t.call(e,n,e)))};function fn(s,t){return t?s.filter(t):s}it.detach=function(s){return fn(this,s).each((t,e)=>{e.parentNode&&e.parentNode.removeChild(e)}),this};const Lp=/^\s*<(\w+)[^>]*>/,Op=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,Sh={"*":mh,tr:wp,td:gh,th:gh,thead:na,tbody:na,tfoot:na};function $h(s){if(!ce(s))return[];if(Op.test(s))return[Hn(RegExp.$1)];const t=Lp.test(s)&&RegExp.$1,e=Sh[t]||Sh["*"];return e.innerHTML=s,g(e.childNodes).detach().get()}g.parseHTML=$h,it.has=function(s){const t=ce(s)?(e,n)=>ra(s,n).length:(e,n)=>n.contains(s);return this.filter(t)},it.not=function(s){const t=ir(s);return this.filter((e,n)=>(!ce(s)||Xt(n))&&!t.call(n,e,n))};function Je(s,t,e,n){const i=[],r=Kn(t),o=n&&ir(n);for(let a=0,l=s.length;at.selected&&!t.disabled&&!t.parentNode.disabled),"value"):s.value||""}function zp(s){return arguments.length?this.each((t,e)=>{const n=e.multiple&&e.options;if(n||zh.test(e.type)){const i=tr(s)?_h.call(s,String):Qs(s)?[]:[String(s)];n?ne(e.options,(r,o)=>{o.selected=i.indexOf(o.value)>=0},!0):e.checked=i.indexOf(e.value)>=0}else e.value=_e(s)||Qs(s)?"":s}):this[0]&&Th(this[0])}it.val=zp,it.is=function(s){const t=ir(s);return ia.call(this,(e,n)=>t.call(e,n,e))},g.guid=1;function Be(s){return s.length>1?sa.call(s,(t,e,n)=>bh.call(n,t)===e):s}g.unique=Be,it.add=function(s,t){return g(Be(this.get().concat(g(s,t).get())))},it.children=function(s){return fn(g(Be(Je(this,t=>t.children))),s)},it.parent=function(s){return fn(g(Be(Je(this,"parentNode"))),s)},it.index=function(s){const t=s?g(s)[0]:this[0],e=s?this:g(t).parent().children();return bh.call(e,t)},it.closest=function(s){const t=this.filter(s);if(t.length)return t;const e=this.parent();return e.length?e.closest(s):t},it.siblings=function(s){return fn(g(Be(Je(this,t=>g(t).parent().children().not(t)))),s)},it.find=function(s){return g(Be(Je(this,t=>ra(s,t))))};const Fp=/^\s*\s*$/g,jp=/^$|^module$|\/(java|ecma)script/i,Bp=["type","src","nonce","noModule"];function Hp(s,t){const e=g(s);e.filter("script").add(e.find("script")).each((n,i)=>{if(jp.test(i.type)&&ph.contains(i)){const r=Hn("script");r.text=i.textContent.replace(Fp,""),ne(Bp,(o,a)=>{i[a]&&(r[a]=i[a])}),t.head.insertBefore(r,null),t.head.removeChild(r)}})}function Wp(s,t,e,n,i){n?s.insertBefore(t,e?s.firstChild:null):s.nodeName==="HTML"?s.parentNode.replaceChild(t,s):s.parentNode.insertBefore(t,e?s:s.nextSibling),i&&Hp(t,s.ownerDocument)}function pn(s,t,e,n,i,r,o,a){return ne(s,(l,h)=>{ne(g(h),(f,d)=>{ne(g(t),(m,b)=>{const w=e?d:b,S=e?b:d,x=e?f:m;Wp(w,x?S.cloneNode(!0):S,n,i,!x)},a)},o)},r),t}it.after=function(){return pn(arguments,this,!1,!1,!1,!0,!0)},it.append=function(){return pn(arguments,this,!1,!1,!0)};function Kp(s){if(!arguments.length)return this[0]&&this[0].innerHTML;if(_e(s))return this;const t=/]/.test(s);return this.each((e,n)=>{Xt(n)&&(t?g(n).empty().append(s):n.innerHTML=s)})}it.html=Kp,it.appendTo=function(s){return pn(arguments,this,!0,!1,!0)},it.wrapInner=function(s){return this.each((t,e)=>{const n=g(e),i=n.contents();i.length?i.wrapAll(s):n.append(s)})},it.before=function(){return pn(arguments,this,!1,!0)},it.wrapAll=function(s){let t=g(s),e=t[0];for(;e.children.length;)e=e.firstElementChild;return this.first().before(t),this.appendTo(e)},it.wrap=function(s){return this.each((t,e)=>{const n=g(s)[0];g(e).wrapAll(t?n.cloneNode(!0):n)})},it.insertAfter=function(s){return pn(arguments,this,!0,!1,!1,!1,!1,!0)},it.insertBefore=function(s){return pn(arguments,this,!0,!0)},it.prepend=function(){return pn(arguments,this,!1,!0,!0,!0,!0)},it.prependTo=function(s){return pn(arguments,this,!0,!0,!0,!1,!1,!0)},it.contents=function(){return g(Be(Je(this,s=>s.tagName==="IFRAME"?[s.contentDocument]:s.tagName==="TEMPLATE"?s.content.childNodes:s.childNodes)))},it.next=function(s,t,e){return fn(g(Be(Je(this,"nextElementSibling",t,e))),s)},it.nextAll=function(s){return this.next(s,!0)},it.nextUntil=function(s,t){return this.next(t,!0,s)},it.parents=function(s,t){return fn(g(Be(Je(this,"parentElement",!0,t))),s)},it.parentsUntil=function(s,t){return this.parents(t,s)},it.prev=function(s,t,e){return fn(g(Be(Je(this,"previousElementSibling",t,e))),s)},it.prevAll=function(s){return this.prev(s,!0)},it.prevUntil=function(s,t){return this.prev(t,!0,s)},it.map=function(s){return g(Cp.apply([],_h.call(this,(t,e)=>s.call(t,e,t))))},it.clone=function(){return this.map((s,t)=>t.cloneNode(!0))},it.offsetParent=function(){return this.map((s,t)=>{let e=t.offsetParent;for(;e&&Ze(e,"position")==="static";)e=e.offsetParent;return e||ph})},it.slice=function(s,t){return g(vh.call(this,s,t))};const Vp=/-([a-z])/g;function ca(s){return s.replace(Vp,(t,e)=>e.toUpperCase())}it.ready=function(s){const t=()=>setTimeout(s,0,g);return Xe.readyState!=="loading"?t():Xe.addEventListener("DOMContentLoaded",t),this},it.unwrap=function(){return this.parent().each((s,t)=>{if(t.tagName==="BODY")return;const e=g(t);e.replaceWith(e.children())}),this},it.offset=function(){const s=this[0];if(!s)return;const t=s.getBoundingClientRect();return{top:t.top+Ji.pageYOffset,left:t.left+Ji.pageXOffset}},it.position=function(){const s=this[0];if(!s)return;const t=Ze(s,"position")==="fixed",e=t?s.getBoundingClientRect():this.offset();if(!t){const n=s.ownerDocument;let i=s.offsetParent||n.documentElement;for(;(i===n.body||i===n.documentElement)&&Ze(i,"position")==="static";)i=i.parentNode;if(i!==s&&Xt(i)){const r=g(i).offset();e.top-=r.top+je(i,"borderTopWidth"),e.left-=r.left+je(i,"borderLeftWidth")}}return{top:e.top-je(s,"marginTop"),left:e.left-je(s,"marginLeft")}};const Nh={class:"className",contenteditable:"contentEditable",for:"htmlFor",readonly:"readOnly",maxlength:"maxLength",tabindex:"tabIndex",colspan:"colSpan",rowspan:"rowSpan",usemap:"useMap"};it.prop=function(s,t){if(s){if(ce(s))return s=Nh[s]||s,arguments.length<2?this[0]&&this[0][s]:this.each((e,n)=>{n[s]=t});for(const e in s)this.prop(e,s[e]);return this}},it.removeProp=function(s){return this.each((t,e)=>{delete e[Nh[s]||s]})};const Up=/^--/;function ha(s){return Up.test(s)}const ua={},{style:qp}=mh,Gp=["webkit","moz","ms"];function Yp(s,t=ha(s)){if(t)return s;if(!ua[s]){const e=ca(s),n=`${e[0].toUpperCase()}${e.slice(1)}`,i=`${e} ${Gp.join(`${n} `)}${n}`.split(" ");ne(i,(r,o)=>{if(o in qp)return ua[s]=o,!1})}return ua[s]}const Qp={animationIterationCount:!0,columnCount:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0};function Eh(s,t,e=ha(s)){return!e&&!Qp[s]&&wh(t)?`${t}px`:t}function Xp(s,t){if(ce(s)){const e=ha(s);return s=Yp(s,e),arguments.length<2?this[0]&&Ze(this[0],s,e):s?(t=Eh(s,t,e),this.each((n,i)=>{Xt(i)&&(e?i.style.setProperty(s,t):i.style[s]=t)})):this}for(const e in s)this.css(e,s[e]);return this}it.css=Xp;function Ph(s,t){try{return s(t)}catch{return t}}const Zp=/^\s+|\s+$/;function Mh(s,t){const e=s.dataset[t]||s.dataset[ca(t)];return Zp.test(e)?e:Ph(JSON.parse,e)}function Jp(s,t,e){e=Ph(JSON.stringify,e),s.dataset[ca(t)]=e}function tm(s,t){if(!s){if(!this[0])return;const e={};for(const n in this[0].dataset)e[n]=Mh(this[0],n);return e}if(ce(s))return arguments.length<2?this[0]&&Mh(this[0],s):_e(t)?this:this.each((e,n)=>{Jp(n,s,t)});for(const e in s)this.data(e,s[e]);return this}it.data=tm;function Ah(s,t){const e=s.documentElement;return Math.max(s.body[`scroll${t}`],e[`scroll${t}`],s.body[`offset${t}`],e[`offset${t}`],e[`client${t}`])}ne([!0,!1],(s,t)=>{ne(["Width","Height"],(e,n)=>{const i=`${t?"outer":"inner"}${n}`;it[i]=function(r){if(this[0])return gs(this[0])?t?this[0][`inner${n}`]:this[0].document.documentElement[`client${n}`]:Wn(this[0])?Ah(this[0],n):this[0][`${t?"offset":"client"}${n}`]+(r&&t?je(this[0],`margin${e?"Top":"Left"}`)+je(this[0],`margin${e?"Bottom":"Right"}`):0)}})}),ne(["Width","Height"],(s,t)=>{const e=t.toLowerCase();it[e]=function(n){if(!this[0])return _e(n)?void 0:this;if(!arguments.length)return gs(this[0])?this[0].document.documentElement[`client${t}`]:Wn(this[0])?Ah(this[0],t):this[0].getBoundingClientRect()[e]-Ch(this[0],!s);const i=parseInt(n,10);return this.each((r,o)=>{if(!Xt(o))return;const a=Ze(o,"boxSizing");o.style[e]=Eh(e,i+(a==="border-box"?Ch(o,!s):0))})}});const Ih="___cd";it.toggle=function(s){return this.each((t,e)=>{if(!Xt(e))return;const n=kh(e);(_e(s)?n:s)?(e.style.display=e[Ih]||"",kh(e)&&(e.style.display=Rp(e.tagName))):n||(e[Ih]=Ze(e,"display"),e.style.display="none")})},it.hide=function(){return this.toggle(!1)},it.show=function(){return this.toggle(!0)};const Dh="___ce",da=".",fa={focus:"focusin",blur:"focusout"},Rh={mouseenter:"mouseover",mouseleave:"mouseout"},em=/^(mouse|pointer|contextmenu|drag|drop|click|dblclick)/i;function pa(s){return Rh[s]||fa[s]||s}function ma(s){const t=s.split(da);return[t[0],t.slice(1).sort()]}it.trigger=function(s,t){if(ce(s)){const[n,i]=ma(s),r=pa(n);if(!r)return this;const o=em.test(r)?"MouseEvents":"HTMLEvents";s=Xe.createEvent(o),s.initEvent(r,!0,!0),s.namespace=i.join(da),s.___ot=n}s.___td=t;const e=s.___ot in fa;return this.each((n,i)=>{e&&Kn(i[s.___ot])&&(i[`___i${s.type}`]=!0,i[s.___ot](),i[`___i${s.type}`]=!1),i.dispatchEvent(s)})};function Lh(s){return s[Dh]=s[Dh]||{}}function nm(s,t,e,n,i){const r=Lh(s);r[t]=r[t]||[],r[t].push([e,n,i]),s.addEventListener(t,i)}function Oh(s,t){return!t||!ia.call(t,e=>s.indexOf(e)<0)}function rr(s,t,e,n,i){const r=Lh(s);if(t)r[t]&&(r[t]=r[t].filter(([o,a,l])=>{if(i&&l.guid!==i.guid||!Oh(o,e)||n&&n!==a)return!0;s.removeEventListener(t,l)}));else for(t in r)rr(s,t,e,n,i)}it.off=function(s,t,e){if(_e(s))this.each((n,i)=>{!Xt(i)&&!Wn(i)&&!gs(i)||rr(i)});else if(ce(s))Kn(t)&&(e=t,t=""),ne(sr(s),(n,i)=>{const[r,o]=ma(i),a=pa(r);this.each((l,h)=>{!Xt(h)&&!Wn(h)&&!gs(h)||rr(h,a,o,t,e)})});else for(const n in s)this.off(n,s[n]);return this},it.remove=function(s){return fn(this,s).detach().off(),this},it.replaceWith=function(s){return this.before(s).remove()},it.replaceAll=function(s){return g(s).replaceWith(this),this};function sm(s,t,e,n,i){if(!ce(s)){for(const r in s)this.on(r,t,e,s[r],i);return this}return ce(t)||(_e(t)||Qs(t)?t="":_e(e)?(e=t,t=""):(n=e,e=t,t="")),Kn(n)||(n=e,e=void 0),n?(ne(sr(s),(r,o)=>{const[a,l]=ma(o),h=pa(a),f=a in Rh,d=a in fa;h&&this.each((m,b)=>{if(!Xt(b)&&!Wn(b)&&!gs(b))return;const w=function(S){if(S.target[`___i${S.type}`])return S.stopImmediatePropagation();if(S.namespace&&!Oh(l,S.namespace.split(da))||!t&&(d&&(S.target!==b||S.___ot===h)||f&&S.relatedTarget&&b.contains(S.relatedTarget)))return;let x=b;if(t){let N=S.target;for(;!xh(N,t);)if(N===b||(N=N.parentNode,!N))return;x=N}Object.defineProperty(S,"currentTarget",{configurable:!0,get(){return x}}),Object.defineProperty(S,"delegateTarget",{configurable:!0,get(){return b}}),Object.defineProperty(S,"data",{configurable:!0,get(){return e}});const k=n.call(x,S,S.___td);i&&rr(b,h,l,t,w),k===!1&&(S.preventDefault(),S.stopPropagation())};w.guid=n.guid=n.guid||g.guid++,nm(b,h,l,t,w)})}),this):this}it.on=sm;function im(s,t,e,n){return this.on(s,t,e,n,!0)}it.one=im;const rm=/\r?\n/g;function om(s,t){return`&${encodeURIComponent(s)}=${encodeURIComponent(t.replace(rm,`\r -`))}`}const am=/file|reset|submit|button|image/i,zh=/radio|checkbox/i;it.serialize=function(){let s="";return this.each((t,e)=>{ne(e.elements||[e],(n,i)=>{if(i.disabled||!i.name||i.tagName==="FIELDSET"||am.test(i.type)||zh.test(i.type)&&!i.checked)return;const r=Th(i);if(!_e(r)){const o=tr(r)?r:[r];ne(o,(a,l)=>{s+=om(i.name,l)})}})}),s.slice(1)},window.$=g;function Fh(s,t){if(s==null)return[s,void 0];typeof t=="string"&&(t=t.split("."));const e=t.join(".");let n=s;const i=[n];for(;typeof n=="object"&&n!==null&&t.length;){let r=t.shift(),o;const a=r.indexOf("[");if(a>0&&a{const i=e[n]??"";s=s.replace(new RegExp(`\\{${n}\\}`,"g"),`${i}`)}),s}for(let e=0;e(s[s.B=1]="B",s[s.KB=1024]="KB",s[s.MB=1048576]="MB",s[s.GB=1073741824]="GB",s[s.TB=1099511627776]="TB",s))(ya||{});function fe(s,t=2,e){return Number.isNaN(s)?"?KB":(e||(s<1024?e="B":s<1048576?e="KB":s<1073741824?e="MB":s<1099511627776?e="GB":e="TB"),(s/ya[e]).toFixed(t)+e)}const ys=s=>{const t=/^[0-9]*(B|KB|MB|GB|TB)$/;s=s.toUpperCase();const e=s.match(t);if(!e)return 0;const n=e[1];return s=s.replace(n,""),Number.parseInt(s,10)*ya[n]};let ba=(document.documentElement.getAttribute("lang")||"zh_cn").toLowerCase().replace("-","_"),tn;function lm(){return ba}function cm(s){ba=s.toLowerCase().replace("-","_")}function jh(s,t){tn||(tn={}),typeof s=="string"&&(s={[s]:t??{}}),g.extend(!0,tn,s)}function Nt(s,t,e,n,i,r){Array.isArray(s)?tn&&s.unshift(tn):s=tn?[tn,s]:[s],typeof e=="string"&&(r=i,i=n,n=e,e=void 0);const o=i||ba;let a;for(const l of s){if(!l)continue;const h=l[o]||l.default;if(!h)continue;const f=r&&l===tn?`${r}.${t}`:t;if(a=ga(h,f),a!==void 0)break}return a===void 0?n:e?Tt(a,...Array.isArray(e)?e:[e]):a}function hm(s,t,e,n){return Nt(void 0,s,t,e,n)}Nt.addLang=jh,Nt.getLang=hm,Nt.getCode=lm,Nt.setCode=cm,Nt.map=tn,jh({zh_cn:{confirm:"确定",save:"保存",cancel:"取消",delete:"删除",reset:"重置",add:"添加",copy:"复制",close:"关闭"},zh_tw:{confirm:"確定",save:"儲存",cancel:"取消",delete:"刪除",reset:"重置",add:"添加",Copy:"複製",close:"關閉"},en:{confirm:"Confirm",save:"Save",cancel:"Cancel",delete:"Delete",reset:"Reset",add:"Add",copy:"Copy",close:"Close"}});function Bh(s,t,e){s instanceof Headers?s.set(t,e):Array.isArray(s)?s.push([t,e]):s[t]=e}function bs(s,t,e){e!=null&&(Array.isArray(e)?e.forEach(n=>bs(s,t,n)):!(e instanceof Blob)&&g.isPlainObject(e)?Object.entries(e).forEach(([n,i])=>{bs(s,`${t}[${n}]`,i)}):s.append(t,e instanceof Blob?e:String(e)))}function um(s,t){if(s){const e={text:"text/plain",html:"text/html",json:"application/json",...t};for(const[n,i]of Object.entries(e))if(i.split(",").map(r=>r.trim()).includes(s))return n}return"text"}function or(s,t){const e=t||new FormData;return s&&(typeof s=="string"&&(s=new URLSearchParams(s)),s instanceof URLSearchParams?s.forEach((n,i)=>{bs(e,i,n)}):Array.isArray(s)?s.forEach(([n,i])=>{bs(e,n,i)}):s instanceof FormData?s.forEach((n,i)=>{bs(e,i,n)}):g.isPlainObject(s)&&Object.entries(s).forEach(([n,i])=>{bs(e,n,i)})),e}class ar{get completed(){return this.data!==void 0||this.error!==void 0}get[Symbol.toStringTag](){return"Ajax"}constructor(t){this.setting=t,this._controller=new AbortController,this._callbacks={success:[],error:[],complete:[]}}on(t,e){return this._callbacks[t].push(e),this}success(t){return this.on("success",t)}done(t){return this.success(t)}fail(t){return this.on("error",t)}complete(t){return this.on("complete",t)}always(t){return this.complete(t)}then(t,e){return this.completed?e&&this.error?e(this.error):t(this.data):(this.success(n=>t(n)),e&&this.fail(e)),this}catch(t){return this.error?(t(this.error),this):this.on("error",e=>t(e))}finally(t){return this.completed?(t(),this):this.complete(()=>t())}abort(t){return this.completed?!1:(this._abortError=t,this._controller.abort(),!0)}getResponseHeader(t){var e;return(e=this.response)==null?void 0:e.headers.get(t)}_init(){if(this.completed)return;const{url:t,type:e,data:n,processData:i=!0,contentType:r,crossDomain:o,accepts:a,dataType:l,timeout:h,dataFilter:f,beforeSend:d,success:m,error:b,complete:w,...S}=this.setting;if((d==null?void 0:d(S))===!1)return;e&&(S.method=e);let x=n;x&&(i&&(x=or(x)),S.body=x),o&&(S.mode="cors");const k=S.headers||{};Bh(k,"X-Requested-With","XMLHttpRequest"),r&&Bh(k,"Content-Type",r),S.headers=k,S.signal&&S.signal.addEventListener("abort",()=>{this.abort()}),m&&this.success(m),b&&this.fail(b),w&&this.complete(w),S.signal=this._controller.signal,this.url=t,this.request=S}_emit(t,...e){this._callbacks[t].forEach(n=>{n.call(this,...e)})}async send(){var f;if(this.completed)return[];this._init();const{timeout:t,dataType:e,accepts:n,dataFilter:i,throws:r,jsonParser:o}=this.setting;t&&(this._timeoutID=window.setTimeout(()=>{this.abort(new Error("timeout"))},t));let a,l,h;try{a=await fetch(this.url,this.request),this.response=a;const{statusText:d}=a;if(a.ok){const m=(f=a.headers.get("Content-Disposition"))==null?void 0:f.startsWith("attachment"),b=m?"blob":e||um(a.headers.get("Content-Type"),n);m||b==="blob"||b==="file"?h=await a.blob():b==="json"?typeof o=="function"?(h=await a.text(),h=o(h)):h=await a.json():h=await a.text(),this.data=h;const w=(i==null?void 0:i(h,b))??h;this._emit("success",w,d,a)}else throw this.data=await a.text(),new Error(d)}catch(d){this.data===void 0&&h!==void 0&&(this.data=h),l=d;let m=!1;l.name==="AbortError"&&(this._abortError?l=this._abortError:m=!0),this.error=l,m||this._emit("error",l,a==null?void 0:a.statusText,l.message)}if(this._timeoutID&&clearTimeout(this._timeoutID),this._emit("complete",a,a==null?void 0:a.statusText),l&&r)throw l;return[h,l,a]}}g.ajax=(s,t)=>{t=t||{},typeof s=="string"?t.url=s:g.extend(t,s);const e=new ar(t);return e.send(),e},g.getJSON=(s,t,e)=>(typeof t=="function"&&(e=t,t=void 0),g.ajax({url:s,data:t,success:e,dataType:"json"})),g.get=(s,t,e,n,i="GET")=>{let r,o;return typeof t=="function"?(r=t,o=void 0):o=t,typeof e=="function"?(r=e,n=void 0):n=e,g.ajax({method:i,url:s,data:o,success:r,dataType:n})},g.post=(s,t,e,n)=>g.get(s,t,e,n,"POST"),g.fn.load=function(s,t,e){typeof t=="function"&&(e=t,t=void 0);const[n,i]=s.split(" ");return g.get(n,t,(r,o,a)=>{i&&(r=g(r).find(i).html()),g(this).html(r).zuiInit(),e==null||e.call(this,r,o,a)},"html"),this};async function Vn(s,t=[],e){const n={throws:!0,dataType:"json"};if(typeof s=="string")n.url=s;else if(typeof s=="object")g.extend(n,s);else if(typeof s=="function"){const o=s(...t);return o instanceof Promise?await o:o}e&&g.extend(n,typeof e=="function"?e(n):e);const i=new ar(n),[r]=await i.send();return r}function _a(s){return!!(s&&(typeof s=="string"||typeof s=="object"&&s.url||typeof s=="function"))}g.fetch=Vn;function pe(){return g.guid++}function lr(s,t){if(s===t)return!1;if(s&&t){const e=typeof s,n=typeof t;if(e!==n)return!0;if(e==="object"&&n==="object"){const i=Array.isArray(s),r=Array.isArray(t);if(i!==r)return!0;if(i&&r){if(s.length!==t.length)return!0;for(let l=0;llr(n instanceof Un?n.value:n,e[i])))&&(this._value=this._compute(),this._lastDependencies=t.map(n=>n instanceof Un?n.cache:n)),this._value}}function va(...s){const t=[],e=new Map,n=(i,r)=>{if(Array.isArray(i)&&(r=i[1],i=i[0]),!i.length)return;const o=e.get(i);typeof o=="number"?t[o][1]=!!r:(e.set(i,t.length),t.push([i,!!r]))};return s.forEach(i=>{typeof i=="function"&&(i=i()),Array.isArray(i)?va(...i).forEach(n):i&&typeof i=="object"?Object.entries(i).forEach(n):typeof i=="string"&&i.split(" ").forEach(r=>n(r,!0))}),t.sort((i,r)=>(e.get(i[0])||0)-(e.get(r[0])||0))}const G=(...s)=>va(...s).reduce((t,[e,n])=>(n&&t.push(e),t),[]).join(" ");g.classes=G,g.fn.setClass=function(s,...t){return this.each((e,n)=>{const i=g(n);s===!0?i.attr("class",G(i.attr("class"),...t)):i.addClass(G(s,...t))})};const _s=new WeakMap;function cr(s,t,e){const n=_s.has(s),i=n?_s.get(s):{};typeof t=="string"?i[t]=e:t===null?Object.keys(i).forEach(r=>{delete i[r]}):Object.assign(i,t),Object.keys(i).forEach(r=>{i[r]===void 0&&delete i[r]}),Object.keys(i).length?(!n&&s instanceof Element&&Object.assign(i,g(s).dataset(),i),_s.set(s,i)):_s.delete(s)}function Xs(s,t,e){let n=_s.get(s)||{};return e&&s instanceof Element&&(n=Object.assign({},g(s).dataset(),n)),t===void 0?n:n[t]}function dm(s){_s.delete(s)}g.fn.dataset=g.fn.data,g.fn.data=function(...s){const[t,e]=s;return!s.length||s.length===1&&typeof t=="string"?this.length?Xs(this[0],t,!0):void 0:this.each((n,i)=>cr(i,t,e))},g.fn.removeData=function(s=null){return this.each((t,e)=>cr(e,s))};function qn(s,...t){return s.includes("RAWJS")&&(s=s.split('"RAWJS<').join("").split('>RAWJS"').join("").split("").join('"').split("").join(` -`)),new Function(`return ${s}`)(...t)}function wa(s,...t){return s.includes("RAWJS")?qn(s,...t):JSON.parse(s)}function vs(s,t){const e=g(s)[0];if(!e)return;const{prefix:n,getter:i,evalValue:r,json:o=!0,evalArgs:a=[]}={prefix:"z-",...typeof t=="string"?{prefix:t}:t},l=Array.isArray(r)?new Set(r):void 0;return Array.from(e.attributes).reduce((h,f)=>{let{name:d}=f;const{value:m}=f;let b=m;if(d.startsWith(n)){if(d=d.slice(n.length).replace(/-([a-z])/g,w=>w[1].toUpperCase()),i)b=i(d,m);else try{r&&(!l||l.has(d))||r===void 0&&m.includes("RAWJS")?b=qn(m,...a):o&&(b=JSON.parse(m))}catch{}h[d]=b}return h},{})}function Ca(s,t,e="z-"){const n=g(s);Object.keys(t).forEach(i=>{let r=t[i];typeof r=="function"&&(r=`RAWJS<${r}>RAWJS`),typeof r!="string"&&(r=JSON.stringify(r)),i=i.replace(/[A-Z]/g,o=>`-${o.toLowerCase()}`),n.attr(`${e}${i}`,r)})}function fm(...s){var e;const t=s.length;if(!t)return vs(this);if(t===1){const[n]=s;return typeof n=="string"?(e=vs(this))==null?void 0:e[n]:(g.isPlainObject(n)&&Ca(this,n),this)}return Ca(this,{[s[0]]:s[1]}),this}g.fn.z=fm,g.fn._attr=g.fn.attr,g.fn.extend({attr(...s){const[t,e]=s;return!s.length||s.length===1&&typeof t=="string"?this._attr.apply(this,s):typeof t=="object"?(t&&Object.keys(t).forEach(n=>{const i=t[n];i===null?this.removeAttr(n):this._attr(n,i)}),this):e===null?this.removeAttr(t):this._attr(t,e)}}),g.Event||(g.Event=(s,t)=>{const[e,...n]=s.split("."),i=new Event(e,{bubbles:!0,cancelable:!0});return i.namespace=n.join("."),i.___ot=e,i.___td=t,i});const Zs=(s,t)=>new Promise(e=>{const n=window.setTimeout(e,s);t&&t(n)}),Hh={};g.share=Hh;var hr,Ft,Wh;D.isValidElement=void 0;var Gn,Kh,Vh,ka,xa,Sa,$a,Js={},Uh=[],pm=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i,ur=Array.isArray;function mn(s,t){for(var e in t)s[e]=t[e];return s}function qh(s){var t=s.parentNode;t&&t.removeChild(s)}function P(s,t,e){var n,i,r,o={};for(r in t)r=="key"?n=t[r]:r=="ref"?i=t[r]:o[r]=t[r];if(arguments.length>2&&(o.children=arguments.length>3?hr.call(arguments,2):e),typeof s=="function"&&s.defaultProps!=null)for(r in s.defaultProps)o[r]===void 0&&(o[r]=s.defaultProps[r]);return dr(s,o,n,i,null)}function dr(s,t,e,n,i){var r={type:s,props:t,key:e,ref:n,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,constructor:void 0,__v:i??++Wh,__i:-1,__u:0};return i==null&&Ft.vnode!=null&&Ft.vnode(r),r}function St(){return{current:null}}function gn(s){return s.children}function $t(s,t){this.props=s,this.context=t}function Yn(s,t){if(t==null)return s.__?Yn(s.__,s.__i+1):null;for(var e;tt&&Gn.sort(ka));fr.__r=0}function Qh(s,t,e,n,i,r,o,a,l,h,f){var d,m,b,w,S,x=n&&n.__k||Uh,k=t.length;for(e.__d=l,mm(e,t,x),l=e.__d,d=0;d0?dr(i.type,i.props,i.key,i.ref?i.ref:null,i.__v):i)!=null?(i.__=s,i.__b=s.__b+1,a=gm(i,e,o,f),i.__i=a,r=null,a!==-1&&(f--,(r=e[a])&&(r.__u|=131072)),r==null||r.__v===null?(a==-1&&d--,typeof i.type!="function"&&(i.__u|=65536)):a!==o&&(a==o-1?d=a-o:a==o+1?d++:a>o?f>l-o?d+=a-o:d--:a(l!=null&&!(131072&l.__u)?1:0))for(;o>=0||a=0){if((l=t[o])&&!(131072&l.__u)&&i==l.key&&r===l.type)return o;o--}if(a{!e||typeof e!="object"||Object.keys(e).forEach(n=>{let i=e[n];const r=s[n];i!==r&&(r!==void 0&&(n==="className"||n.endsWith("Class")?i=[r,i]:n==="children"?i=[...pr(r),...pr(i)]:typeof r=="object"&&(n==="style"||n.endsWith("Style")||n==="attrs"||n.endsWith("Attrs")||n==="props")&&(i=g.extend(r,i))),s[n]=i)})}),s}function Pa(s){return Object.keys(s).forEach(t=>{s[t]===void 0&&delete s[t]}),s}function eu(s,t=!0){const e=g(s),n=e[0],i="zui-disable-scroll";if(t){if(e.data(i))return;if((e.css("scrollbar-gutter")||"").includes("stable")){e.data(i,{overflow:e.css("overflow")}).css("overflow","hidden");return}const r=n===document.body||e.is("html")?window.innerWidth-document.body.clientWidth:n.offsetWidth-n.clientWidth;if(!r)return;const o=e.css("paddingRight")||"0";e.data(i,{paddingRight:o,overflow:e.css("overflow")}).css({paddingRight:`${r+Number.parseInt(o,10)}px`,overflow:"hidden"})}else{const r=e.data(i);if(!r)return;e.css(r).removeData(i)}}g.fn.disableScroll=function(s=!0){return this.each((t,e)=>{eu(e,s)})},g.fn.enableScroll=function(s=!0){return this.disableScroll(!s)};function Ma(s,t,e){if(!(e.on||"click").split(" ").includes(t.type))return;const n=e.selector?g(t.target).closest(e.selector):s;if(!n.length)return;const i=h=>h===""?!0:h,r=h=>{if(typeof h=="string")try{h=JSON.parse(h)}catch{}return h};if(i(e.once)){if(e.onceCalled)return;s.dataset("once-called",!0)}if(i(e.prevent)&&t.preventDefault(),i(e.stop)&&t.stopPropagation(),i(e.self)&&t.currentTarget!==t.target)return;const o=[["$element",s],["event",t],["options",e],["$target",n]],a=h=>typeof h=="function"?h(...o):g.runJS(h,...o);if(e.if!==void 0&&!a(e.if))return;const l=e.call;if(l){let h;if(typeof l=="string"?h=/^[$A-Z_][0-9A-Z_$.]*$/i.test(l)?ga(window,l):a(l):h=l,typeof h=="function"){const f=[],d=e.params;e.params=f,typeof d=="string"&&d.length?d[0]==="["?f.push(...r(d)):f.push(...d.split(", ").map(m=>(m=m.trim(),m==="$element"?s:m==="event"?t:m==="options"?e:m.startsWith("$element.")||m.startsWith("event.")||m.startsWith("options.")?a(m):r(m)))):Array.isArray(d)?f.push(...d):f.push(d),h(...f)}}e.do&&a(e.do)}function _m(s){const t=g(this),e=s.type,n=t.attr("zui-on");if(n){const[o,a]=n.split("~").map(l=>l.trim());o&&Ma(t,s,g.extend({on:o},a?a.startsWith("{")?qn(a):{do:a}:vs(t,{prefix:"data-",evalValue:["call","if","do"]})))}const i=t.attr(`zui-on-${e}`);i&&Ma(t,s,g.extend({on:e},i.startsWith("{")?qn(i):{do:i})),t.attr("data-on")&&Ma(t,s,vs(t,{prefix:"data-",evalValue:["call","if","do"]}))}function nu(s){g(document).off(".zui.global").on(s.map(t=>`${t}.zui.global`).join(" "),`[zui-on],${s.map(t=>`[zui-on-${t}]`)},[data-on]`,_m)}nu(["click","change","inited"]);function Xn(s){if(typeof s=="function")return Xn(s());if(typeof s=="number")return[s];let t=s.match(/(\d+)(%|px)?/);return t?[parseInt(t[1]),t[2]]:(t=s.match(/(\d+)\/(\d+)/),t?[100*parseInt(t[1])/parseInt(t[2]),"%"]:[NaN])}function qt(s){if(s==null)return null;const[t,e="px"]=Xn(s);return Number.isNaN(t)?typeof s=="string"?s:null:`${t}${e}`}async function gr(s,t){var n,i,r;if(s instanceof Blob){const o=document.createElement("a");return o.href=window.URL.createObjectURL(s),t&&(o.download=decodeURIComponent(t)),o.click(),o.remove(),s}if(s instanceof Response){const o=await s.blob();return t=t||((r=(i=(n=s.headers.get("Content-Disposition"))==null?void 0:n.split(";")[1])==null?void 0:i.split("=")[1])==null?void 0:r.replace(/"/g,"")),gr(o,t)}const e=await fetch(s);return gr(e)}class su{constructor(t){this._$target=g(t)}on(...t){return this._$target.on(...t),this}one(...t){return this._$target.one(...t),this}off(...t){return this._$target.off(...t),this}trigger(...t){return this._$target.trigger(...t),this}}const He=new su(document);g.bus=He,g.on=He.on.bind(He),g.one=He.one.bind(He),g.off=He.off.bind(He),g.trigger=He.trigger.bind(He);var vm=["Shift","Meta","Alt","Control"],iu=typeof navigator=="object"?navigator.platform:"",ru=/Mac|iPod|iPhone|iPad/.test(iu),wm=ru?"Meta":"Control",Cm=iu==="Win32"?["Control","Alt"]:ru?["Alt"]:[];function Aa(s,t){return typeof s.getModifierState=="function"&&(s.getModifierState(t)||Cm.includes(t)&&s.getModifierState("AltGraph"))}function km(s){return s.trim().split(" ").map(function(t){var e=t.split(/\b\+/),n=e.pop();return[e=e.map(function(i){return i==="$mod"?wm:i}),n]})}function ou(s,t){var e;t===void 0&&(t={});var n=(e=t.timeout)!=null?e:1e3,i=Object.keys(s).map(function(a){return[km(a),s[a]]}),r=new Map,o=null;return function(a){a instanceof KeyboardEvent&&(i.forEach(function(l){var h=l[0],f=l[1],d=r.get(h)||h;(function(m,b){return!(b[1].toUpperCase()!==m.key.toUpperCase()&&b[1]!==m.code||b[0].find(function(w){return!Aa(m,w)})||vm.find(function(w){return!b[0].includes(w)&&b[1]!==w&&Aa(m,w)}))})(a,d[0])?d.length>1?r.set(h,d.slice(1)):(r.delete(h),f(a)):Aa(a,a.key)||r.delete(h)}),o&&clearTimeout(o),o=setTimeout(r.clear.bind(r),n))}}function xm(s,t,e){var n;e===void 0&&(e={});var i=(n=e.event)!=null?n:"keydown",r=ou(t,e);return s.addEventListener(i,r),function(){s.removeEventListener(i,r)}}function Ia(s,t={}){if(!s)return;const e=Object.keys(t).reduce((n,i)=>(t[i].optional||(n[i]={...t[i]}),n),{});return Object.keys(s).forEach(n=>{const i=s[n];i?i===!0?t[n]&&(e[n]={...t[n]}):e[n]=i:delete e[n]}),Object.keys(e).reduce((n,i)=>{const{keys:r,handler:o}=e[i];return typeof r=="string"?n[r]=o:r.forEach(a=>{n[a]=o}),n},{})}function Da(s,t,e){const{timeout:n,event:i="keydown",scope:r,when:o}=e||{},a=ou(t,{timeout:n}),l=`.zui.hotkeys${r?`.${r}`:""}`,h="zui-hotkeys-composing";return g(s).on(`${i}${l}`,function(f){o&&o(f)===!1||g(f.target).data(h)||a(f)}).on(`compositionstart${l}`,f=>{g(f.target).data(h,!0)}).on(`compositionend${l}`,f=>{g(f.target).removeData(h)})}function Ra(s,t){return g(s).off(`.zui.hotkeys${t?`.${t}`:""}`)}const Sm=xm;g.fn.hotkeys=function(s,t){return Da(this,s,t)},g.fn.unbindHotkeys=function(s){return Ra(this,s)},g.hotkeys=function(s,t){Da(window,s,t)},g.unbindHotkeys=function(s){Ra(window,s)};function yr(){return document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement}async function au(s){(typeof s=="string"||s instanceof Element||s instanceof g)&&(s={target:s});const{target:t,onError:e,onSuccess:n,afterExit:i,afterEnter:r}=s,o=g(t),a=o[0];if(!a)return;const l=a.requestFullscreen||a.webkitRequestFullscreen||a.mozRequestFullScreen;if(!l){e==null||e.call(a,new Error("[ZUI] The browser does not support full screen feature."));return}try{await l.call(a),n==null||n.call(a),g(a).off(".zui.fullscreen"),i&&o.on("exitFullscreen.zui.fullscreen",i),r&&o.on("enterFullscreen.zui.fullscreen",r)}catch(h){e==null||e.call(a,h)}document.zuiBindFullscreenChange||(document.zuiBindFullscreenChange=!0,g(document).on("fullscreenchange.zui webkitfullscreenchange.zui mozfullscreenchange.zui",h=>{const f=yr();let d=f;f?g(f).addClass("is-in-fullscreen"):(d=g(document).find(".is-in-fullscreen")[0]||document,g(d).removeClass("is-in-fullscreen")),g("body").toggleClass("has-in-fullscreen",!!f);const m={event:h,target:d,fullscreenElement:f};g(d).trigger(f?"enterFullscreen":"exitFullscreen",m).trigger("toggleFullscreen",m)}))}async function La(s){const t=yr();return s===!1&&!!t===s?s:t?(document.exitFullscreen(),!1):(await au(s),!0)}g.fn.fullscreen=function(s){return La({target:this,...s})},g.getFullscreenElement=yr,g.toggleFullscreen=La;function Zn(s){return!s||s.parentNode===document?!1:s.parentNode?Zn(s.parentNode):!0}g.isDetached=Zn,g.fn.isDetached=function(){const s=this[0];return!s||Zn(s)};const Jn=class hp{constructor(t,e){this._inited=!1,this._autoDestory=0,this._destroyed=!1;const{KEY:n,DATA_KEY:i,DEFAULT:r,MULTI_INSTANCE:o,NAME:a,ATTR_KEY:l,ALL:h,TYPED_ALL:f}=this.constructor;if(!a)throw new Error('[ZUI] The component must have a "NAME" static property.');const d=g(t);if(d.data(n)&&!o)throw new Error(`[ZUI] The component "${a}" has been initialized on element.`);const m=d[0],b=pe();if(this._gid=b,this._element=m,this._options={...r,...(e==null?void 0:e.$optionsFromDataset)!==!1?d.dataset():{}},this.setOptions(e),this._key=this.options.key??`__${b}`,h.has(m)?h.get(m).add(this):h.set(m,new Set([this])),f.has(a)?f.get(a).add(this):f.set(a,new Set([this])),d.data(n,this).attr(l,"").attr(i,`${b}`),o){const w=`${n}:ALL`;let S=d.data(w);S||(S=new Map,d.data(w,S)),S.set(this._key,this)}this.init(),requestAnimationFrame(async()=>{this._inited=!0,await this.afterInit(),this.emit("inited",this.options)})}static get ZUI(){return this.NAME.replace(/(^[A-Z]+)/,t=>t.toLowerCase())}static get KEY(){return`zui.${this.NAME}`}static get NAMESPACE(){return`.zui.${this.ZUI}`}static get DATA_KEY(){return`data-zui-${this.NAME}`}static get ATTR_KEY(){return`z-use-${this.NAME}`}static get SELECTOR(){return`[${this.DATA_KEY}]`}get inited(){return this._inited}get destroyed(){return this._destroyed}get element(){return this._element}get key(){return this._key}get options(){return this._options}get gid(){return this._gid}get $element(){return g(this.element)}get $emitter(){return this.$element}get i18nData(){return[this.options.i18n,this.constructor.i18n]}init(){}afterInit(){}render(t,e){this.setOptions(t,e)}destroy(){const{KEY:t,DATA_KEY:e,ALL:n,TYPED_ALL:i,NAME:r,MULTI_INSTANCE:o,ATTR_KEY:a}=this.constructor,{$element:l,element:h}=this;if(this.emit("destroyed"),this._destroyed=!0,l.off(this.namespace).removeData(t).removeAttr(a).removeAttr(e),o){const m=this.$element.data(`${t}:ALL`);if(m)if(m.delete(this._key),m.size===0)this.$element.removeData(`${t}:ALL`);else{const b=m.values().next().value;l.data(t,b).attr(e,b.gid)}}const f=n.get(h);f&&(f.delete(this),f.size===0&&n.delete(h));const d=i.get(r);d&&(d.delete(this),d.size===0&&i.delete(r))}autoDestroy(t=100){this._autoDestory&&clearTimeout(this._autoDestory),this._autoDestory=window.setTimeout(()=>{this._autoDestory=0,Zn(this.element)&&this.destroy()},t)}setOptions(t,e){return e?this._options={...this.constructor.DEFAULT,...(t==null?void 0:t.$optionsFromDataset)!==!1?this.$element.dataset():{},...t}:t&&g.extend(this._options,t),this._options}emit(t,...e){const n=g.Event(t);return n.__src=this,this.$emitter.trigger(n,[this,...e]),n}on(t,e,n){const i=this;this.$element[n!=null&&n.once?"one":"on"](this._wrapEvent(t),function(r,o){(!r.__src||r.__src===i)&&e.call(this,r,o)})}one(t,e){this.on(t,e,{once:!0})}off(t){this.$element.off(this._wrapEvent(t))}i18n(t,e,n){const{i18nData:i}=this;return Nt(i,t,e,n,this.options.lang,this.constructor.NAME)??Nt(i,t,e,n,this.options.lang)??`{i18n:${t}}`}get namespace(){return`${this.constructor.NAMESPACE}.${this._key}`}_wrapEvent(t){return t.split(" ").map(e=>e.includes(".")?e:`${e}${this.namespace}`).join(" ")}static get(t,e){const n=g(t);if(this.MULTI_INSTANCE&&e!==void 0){const i=n.data(`${this.KEY}:ALL`);return i?i.get(e):void 0}return n.data(this.KEY)}static isValid(t){return!0}static ensure(t,e){const n=this.get(t,e==null?void 0:e.key);if(n){if(this.isValid(n))return e&&n.setOptions(e),n;n.destroy()}return new this(t,e)}static getAll(t,e){var l;const{SELECTOR:n,ALL:i,TYPED_ALL:r}=this,o=[],a=h=>{h instanceof this&&(!e||e(h)!==!1)&&o.push(h)};return t?g(t).find(n).each((h,f)=>{var d;(d=i.get(f))==null||d.forEach(a)}):this!==hp?(l=r.get(this.NAME))==null||l.forEach(a):i.forEach(h=>{h.forEach(a)}),o.sort((h,f)=>h.gid-f.gid)}static query(t,e,n){return t===void 0?this.getAll(void 0,n).pop():this.get(g(t).closest(this.SELECTOR),e)}static defineFn(t){let e=t||this.ZUI;g.fn[e]&&(e=`zui${this.NAME}`);const n=this;g.fn.extend({[e](i,...r){const o=typeof i=="object"?i:void 0,a=typeof i=="string"?i:void 0;let l;return this.each((h,f)=>{let d=n.get(f);if(d?o&&d.render(o):d=new n(f,o),a){let m=d[a],b=d;m===void 0&&(b=d.$,m=b[a]),typeof m=="function"?l=m.call(b,...r):l=m}}),l!==void 0?l:this}})}static register(t,e){var i,r;t=t||this,e=(e??t.NAME).toLowerCase(),this.map.set(e,t);const n=(r=(i=t.toggle)==null?void 0:i.name)==null?void 0:r.toLowerCase();n&&n!==e&&this.toggleMap.set(n,t)}};Jn.DEFAULT={},Jn.MULTI_INSTANCE=!1,Jn.ALL=new Map,Jn.TYPED_ALL=new Map,Jn.map=new Map,Jn.toggleMap=new Map;let Lt=Jn;function ti(s){return Lt.map.get(s.toLowerCase())}function Oa(s,t,e={}){let n=ti(s);if(n||(n=za(s)),!n)return null;const{$update:i,...r}=e;if(!n.MULTI_INSTANCE){const o=n.get(t);if(o)return i&&o.render(r,i==="reset"),o}return new n(t,r)}function $m(s,t,e={}){requestAnimationFrame(()=>Oa(s,t,e))}function lu(s,t){Lt.register(s,t)}function za(s){const{zui:t}=window;if(t){s=s==null?void 0:s.toLowerCase();for(const e in t){const n=e.toLowerCase()===s;if(s&&!n)continue;const i=t[e];if(!(typeof i!="function"||!i.NAME||!i.ZUI)&&(Lt.map.has(e.toLowerCase())||lu(i),n))return i}}}function Tm(s){var t;s?(t=ti(s))==null||t.defineFn():window._zuiDefined||(za(),Lt.map.forEach(e=>{e.defineFn()}),Object.assign(window,{_zuiDefined:!0}))}function Nm(s,t={}){const e=g(s);let n=e.attr("zui-create");const{update:i,onCreate:r}=t,o=(a,l)=>{if(l={$update:i,$optionsFromDataset:!1,...l},r){const f=r(a,l);if(f===!1)return;f&&(l=f)}const h=l.$lib;if(h){delete l.$lib,g.getLib(h).then(()=>Oa(a,s,l));return}$m(a,s,l)};if(typeof n=="string"){n=n.trim();const a=n.length?n.split(",").map(f=>f.trim()):[],l=vs(s,{prefix:"zui-create-",evalValue:!0}),h=Object.keys(l);if(!h.length&&a.length===1)o(a[0],e.dataset());else{const f=new Set;[...a,...h].forEach(d=>{if(f.has(d))return;const m=l[d];o(d,m),delete l[d],f.add(d)})}}else{const a=e.dataset(),l=a==null?void 0:a.zui;if(!l)return;console.warn("[ZUI] create component instance with [data-zui] is deprecated, use [zui-create] instead.",{element:s,options:t}),delete a.zui,o(l,a)}}function Em(){g(document).on("click.zui.toggle mouseenter.zui.toggle","[data-toggle],[zui-toggle]",function(s){const t=g(this),e=t.dataset("toggle")||t.attr("zui-toggle");if(!e)return;const n=Lt.toggleMap.get(e)||ti(e),i=n==null?void 0:n.toggle;if(!i)return;const{trigger:r="click",skip:o="[disabled],.disabled",check:a}=i,l=s.type==="mouseover"?"hover":"click";if(!r.includes(l)||a&&!a.call(n,this,l,s)||o&&t.is(o))return;const{onGet:h,onCreate:f,setOptions:d=!0,getOptions:m,prevent:b=!0,handler:w,onToggle:S,convertHref:x}=i;let k=t.dataset();const N=t.attr(`zui-toggle-${e}`);if(N&&(k=g.extend(k,qn(N))),x&&t.is("a")){const j=t.attr("href");if(j){const K=x===!0?{selector:"target",url:"url"}:x;"#.".includes(j[0])?K.selector&&k[K.selector]===void 0&&(k[K.selector]=j):K.url&&k[K.url]===void 0&&(k[K.url]=j)}}if(m&&(k=m.call(n,this,k,s)),w){w.call(n,this,k,l,s),b&&s.preventDefault();return}let B=h?h.call(n,this):n.get(this);if(B)d&&B.setOptions(k);else{const j=f?f.call(n,this,s,k):new n(this,k);if(!j)return;B=j}if(S){if(S.call(n,B,this,s)===!1)return}else{const{shown:j,show:K,hide:Y,toggle:Q}=B;let Z;if(Q?Z=Q:K&&Y?j?Z=Y:Z=K:K&&(Z=K),Z)Z.call(B);else return}b&&s.preventDefault()})}function Pm(s,t){const e=Xs(s),n=[];return Object.keys(e).forEach(i=>{if(!i.startsWith("zui."))return;const r=e[i];(t==null?void 0:t(r,i))!==!1&&n.push(e[i])}),n}let br=0;function cu(s=100){if(br&&clearTimeout(br),s){br=window.setTimeout(()=>cu(0),s);return}br=0,Lt.ALL.forEach(t=>{t.forEach(e=>e.autoDestroy())})}function Mm(){if(!document.body||Xs(document.body,"_autoDestoryMob"))return;const s=new MutationObserver(t=>{let e=!1;for(const n of t)if(n.removedNodes.length){e=!0;break}e&&cu()});s.observe(document.body,{childList:!0,subtree:!0}),cr(document.body,"_autoDestoryMob",s)}g.fn.zuiInit=function(s){return this.find("[zui-create],[data-zui]").each(function(){var t;((t=s==null?void 0:s.beforeCreate)==null?void 0:t.call(s,this))!==!1&&Nm(this,s)}),this.find("[zui-init]").each(function(){this.hasAttribute("z-zui-inited")||(this.setAttribute("z-zui-inited",""),g.runJS(this.getAttribute("zui-init"),["$element",g(this)]))}),this.find(".hide-before-init").removeClass("invisible hidden opacity-0"),this.find(".scroll-into-view").scrollIntoView(),this.find('[data-on="inited"],[zui-on-inited]').each((t,e)=>{const n=g(e);n.zui()||n.trigger("inited")}),this},g.fn.zui=function(s,t){const e=this[0];if(!e)return;if(typeof s!="string"){const i={};let r;return Pm(e,(o,a)=>{i[a]=o,(!r||r.gid1?e[0]:void 0,i=e[e.length>1?1:0],r=g(this).zui(n),o=r==null?void 0:r[i];typeof o=="function"&&o.apply(r,t)}),this},g(()=>{g("body").zuiInit({update:!0}),Em(),Mm()});class Fa extends Lt{init(){const{offset:t=1,side:e,zIndex:n,pinnedClass:i="is-pinned",targets:r,scrollContainer:o}=this.options,{$element:a}=this,l=r?a.find(r):a;if(l.css({position:"sticky",zIndex:n}),e&&l.css(e,-t),o){const h=a.closest(o)[0];if(h){const f=()=>{this._raf&&cancelAnimationFrame(this._raf),this._raf=requestAnimationFrame(()=>{if(this._raf=0,h.scrollTop===0&&(!e||e==="top")){l.toggleClass(i,!1);return}const d=h.getBoundingClientRect();l.each((m,b)=>{const S=b.getBoundingClientRect()[e||"top"]===d[e||"top"];b.classList.toggle(i,S)})})};this._scrollListener=f,h.addEventListener("scroll",f)}this._container=h}else this._ob=new IntersectionObserver(h=>{h.forEach(f=>{f.target.classList.toggle(i,f.intersectionRatiothis._ob.observe(f))}destroy(){var t;(t=this._ob)==null||t.disconnect(),this._container&&(this._container.removeEventListener("scroll",this._scrollListener),this._raf&&cancelAnimationFrame(this._raf))}}Fa.NAME="Sticky";function ei(s,t={}){const e=g(s)[0];if(!e)return!1;let{viewport:n}=t;const{left:i,top:r,width:o,height:a}=e.getBoundingClientRect();if(t.checkZeroSize&&!(o*a))return!1;if(!n)if(t.container)n=g(e).closest(t.container)[0].getBoundingClientRect();else{const{innerHeight:w,innerWidth:S}=window,{clientHeight:x,clientWidth:k}=document.documentElement;n={left:0,top:0,width:S||k,height:w||x}}const{left:l,top:h,width:f,height:d}=n;if(t.fullyCheck)return i>=l&&r>=h&&i+o<=f+l&&r+a<=d+h;const m=i<=l+f&&i+o>=l;return r<=h+d&&r+a>=h&&m}g.fn.isVisible=function(s){return ei(this,s)};function _r(s,t,e=!1){var i;const n=g(s);if(t!==void 0){if(typeof t=="string"&&t.length){const r=`zui-runjs-${pe()}`;n.append(`