From bf120839010f54f10bf3a509739c1b520e31ccad Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Wed, 29 Jun 2022 02:14:23 +1000 Subject: [PATCH 01/46] Initial conversion started --- traq-ui/ticket-listing/TicketFilters.vue | 10 ++ traq-ui/ticket-listing/TicketListing.vue | 95 +++++++++++++++++++ traq-ui/ticket-listing/ticket-listing.ts | 5 + vendor/traq/controllers/tickets.php | 51 ++-------- vendor/traq/views/default/layouts/_head.phtml | 7 ++ vendor/traq/views/default/tickets/index.phtml | 44 +-------- 6 files changed, 125 insertions(+), 87 deletions(-) create mode 100644 traq-ui/ticket-listing/TicketFilters.vue create mode 100644 traq-ui/ticket-listing/TicketListing.vue create mode 100644 traq-ui/ticket-listing/ticket-listing.ts diff --git a/traq-ui/ticket-listing/TicketFilters.vue b/traq-ui/ticket-listing/TicketFilters.vue new file mode 100644 index 000000000..f8615c0b6 --- /dev/null +++ b/traq-ui/ticket-listing/TicketFilters.vue @@ -0,0 +1,10 @@ + diff --git a/traq-ui/ticket-listing/TicketListing.vue b/traq-ui/ticket-listing/TicketListing.vue new file mode 100644 index 000000000..1cbd1ac35 --- /dev/null +++ b/traq-ui/ticket-listing/TicketListing.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/traq-ui/ticket-listing/ticket-listing.ts b/traq-ui/ticket-listing/ticket-listing.ts new file mode 100644 index 000000000..e679d28ca --- /dev/null +++ b/traq-ui/ticket-listing/ticket-listing.ts @@ -0,0 +1,5 @@ +import { createApp } from "vue"; +import TicketListing from "./TicketListing.vue"; + +const app = createApp(TicketListing); +app.mount("#ticket-listing"); diff --git a/vendor/traq/controllers/tickets.php b/vendor/traq/controllers/tickets.php index 0b0892f48..21f1b58c5 100644 --- a/vendor/traq/controllers/tickets.php +++ b/vendor/traq/controllers/tickets.php @@ -82,7 +82,9 @@ public function __construct() public function action_index() { // Atom feed - $this->feeds[] = array(Request::requestUri() . ".atom", l('x_ticket_feed', $this->project->name)); + $this->feeds[] = [ + Request::requestUri() . ".atom", l('x_ticket_feed', $this->project->name) + ]; // Create ticket filter query $filter_query = new TicketFilterQuery($this->project); @@ -97,8 +99,8 @@ public function action_index() // Any filters stored in the session? if (!count($filter_query->filters()) - and isset($_SESSION['ticket_filters']) - and isset($_SESSION['ticket_filters'][$this->project->id])) { + && isset($_SESSION['ticket_filters']) + && isset($_SESSION['ticket_filters'][$this->project->id])) { foreach (explode('&', $_SESSION['ticket_filters'][$this->project->id]) as $filter_value) { if (strpos($filter_value, '=')) { $filter_value = explode('=', $filter_value); @@ -116,7 +118,7 @@ public function action_index() View::set('filters', $filter_query->filters()); // Fetch tickets - $tickets = array(); + $tickets = []; $rows = $this->db->select('tickets.*')->from('tickets')->custom_sql($filter_query->sql()); // Order by creation date for atom feed @@ -184,47 +186,6 @@ public function action_index() // Send the tickets array to the view.. View::set('tickets', $tickets); - - // Columns - $this->get_columns(); - } - - private function get_columns() - { - $allowed_columns = ticketlist_allowed_columns(); - - // Add custom fields - foreach ($this->custom_fields as $field) { - $allowed_columns[] = $field->id; - } - - // Set columns from form - if (Request::method() == 'post' and isset(Request::$post['update_columns'])) { - $new_columns = array(); - foreach (Request::$post['columns'] as $column) { - $new_columns[] = $column; - } - $_SESSION['columns'] = Request::$request['columns'] = $new_columns; - } - - // Get columns - $columns = array(); - if (isset($_SESSION['columns']) or isset(Request::$request['columns'])) { - // Loop over customs from session or request - foreach ((isset($_SESSION['columns']) ? $_SESSION['columns'] : explode(',', Request::$request['columns'])) as $column) { - // Make sure it's a valid column - if (in_array($column, $allowed_columns)) { - $columns[] = $column; - } - } - } - // Use default columns - else { - $columns = ticket_columns(); - } - - // Send columns to view - View::set('columns', $columns); } /** diff --git a/vendor/traq/views/default/layouts/_head.phtml b/vendor/traq/views/default/layouts/_head.phtml index fc7d4eb54..e83c8bf62 100644 --- a/vendor/traq/views/default/layouts/_head.phtml +++ b/vendor/traq/views/default/layouts/_head.phtml @@ -9,6 +9,13 @@ + + + + + feeds as $feed) { ?> diff --git a/vendor/traq/views/default/tickets/index.phtml b/vendor/traq/views/default/tickets/index.phtml index 96745675c..ffb0779a2 100644 --- a/vendor/traq/views/default/tickets/index.phtml +++ b/vendor/traq/views/default/tickets/index.phtml @@ -1,42 +1,2 @@ -
-

- - -
- - - -permission($project->id, 'perform_mass_actions')) { ?> - - - - - - - - - - -permission($project->id, 'perform_mass_actions')) { ?> - - - - - - - - - - - - - - - - -
"select_all_tickets"))?> - - -
ticket_id, array('id' => "mass_action_ticket_{$ticket->ticket_id}"))?>summary), "{$project->slug}/tickets/{$ticket->ticket_id}"); ?>user->name, 20), $ticket->user->href()); ?>assigned_to ? HTML::link(strshorten($ticket->assigned_to->name, 20), $ticket->assigned_to->href()) :'')?>
- - +
+ \ No newline at end of file From 85f74dcf494cb79124da75a0f0d4d319eae117c9 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Wed, 29 Jun 2022 18:32:23 +1000 Subject: [PATCH 02/46] PHP 8.0+ --- README.md | 2 +- dev/docker/{php73-apache => php80-apache} | 2 +- docker-compose.yml | 15 +-------------- 3 files changed, 3 insertions(+), 16 deletions(-) rename dev/docker/{php73-apache => php80-apache} (74%) diff --git a/README.md b/README.md index beeb323a9..70d0e35e0 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Traq is a PHP powered project manager, capable of tracking issues for multiple p Requirements ------------ -- PHP 7.4+ +- PHP 8.0+ - MariaDB _(or MySQL)_ - Apache mod_rewrite or server configured to use `index.php` as the 404 page. diff --git a/dev/docker/php73-apache b/dev/docker/php80-apache similarity index 74% rename from dev/docker/php73-apache rename to dev/docker/php80-apache index d3f468fcd..7126e11de 100644 --- a/dev/docker/php73-apache +++ b/dev/docker/php80-apache @@ -1,4 +1,4 @@ -FROM php:7.3-apache +FROM php:8.0-apache RUN docker-php-ext-install pdo_mysql diff --git a/docker-compose.yml b/docker-compose.yml index 2e22db575..44078f4e7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,7 @@ services: container_name: traq3_web build: context: . - dockerfile: dev/docker/php73-apache + dockerfile: dev/docker/php80-apache working_dir: /var/www/html volumes: - .:/var/www/html @@ -14,19 +14,6 @@ services: links: - mysql:db - web56: - container_name: traq3_web56 - build: - context: . - dockerfile: dev/docker/php56-apache - working_dir: /var/www/html - volumes: - - .:/var/www/html - ports: - - 3001:80 - links: - - mysql:db - mysql: container_name: traq3_db image: mariadb:10 From d61ebadddacaca063ba7bc230a51ad5fd6e8c4e3 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Thu, 30 Jun 2022 01:00:36 +1000 Subject: [PATCH 03/46] Tooling tweaks --- .eslintrc.cjs | 1 + .prettierrc.json | 9 +- package.json | 5 + postcss.config.js | 7 + tailwind.config.js | 25 ++++ vite.config.ts | 1 + yarn.lock | 309 +++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 343 insertions(+), 14 deletions(-) create mode 100644 postcss.config.js create mode 100644 tailwind.config.js diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 2ec566d8c..a4b6b19bf 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -8,6 +8,7 @@ module.exports = { "eslint:recommended", "@vue/eslint-config-typescript/recommended", ], + parser: "vue-eslint-parser", rules: { "comma-dangle": ["error", "always-multiline"], }, diff --git a/.prettierrc.json b/.prettierrc.json index 6879e2f5b..db1175caf 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,4 +1,11 @@ { + "printWidth": 160, + "tabWidth": 2, + "endOfLine": "lf", + "tabs": false, "semi": false, - "trailingComma": "es5" + "trailingComma": "es5", + "quoteProps": "as-needed", + "bracketSpacing": true, + "arrowParens": "always" } \ No newline at end of file diff --git a/package.json b/package.json index e5c1c2abc..b2fdc80bd 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,16 @@ "@vue/eslint-config-typescript": "^11.0.0", "@vue/test-utils": "^2.0.0", "@vue/tsconfig": "^0.1.3", + "autoprefixer": "^10.4.7", "eslint": "^8.5.0", "eslint-plugin-vue": "^9.0.0", "jsdom": "^20.0.0", "npm-run-all": "^4.1.5", + "postcss": "^8.4.14", + "postcss-nesting": "^10.1.9", "prettier": "^2.5.1", + "sass": "^1.53.0", + "tailwindcss": "^3.1.4", "typescript": "~4.7.4", "vite": "^2.9.12", "vitest": "^0.15.1", diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 000000000..2ac1a49b9 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,7 @@ +module.exports = { + plugins: [ + require("tailwindcss/nesting"), + require("tailwindcss"), + require("autoprefixer"), + ], +} diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 000000000..de4fa018a --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,25 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./traq-ui/**/*.{vue,js,ts,jsx,tsx}"], + theme: { + extend: { + colors: { + brand: { + 100: "#d6e4ee", + 200: "#aec9dd", + 300: "#85aecc", + 400: "#5d93bb", + 500: "#3478aa", + 600: "#2a6088", + 700: "#1f4866", + 800: "#153044", + 900: "#0a1822", + }, + }, + }, + }, + plugins: [], + corePlugins: { + preflight: false, + }, +} diff --git a/vite.config.ts b/vite.config.ts index a63d0795f..80667396c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -19,6 +19,7 @@ export default defineConfig({ manifest: true, rollupOptions: { input: { + main: resolve(__dirname, "traq-ui/main.ts"), "ticket-listing": resolve( __dirname, "traq-ui/ticket-listing/ticket-listing.ts" diff --git a/yarn.lock b/yarn.lock index 56c915dcc..e31e7c130 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.6.tgz#845338edecad65ebffef058d3be851f1d28a63bc" integrity sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw== +"@csstools/selector-specificity@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.1.tgz#b6b8d81780b9a9f6459f4bfe9226ac6aefaefe87" + integrity sha512-aG20vknL4/YjQF9BSV7ts4EWm/yrjagAN7OWBNmlbEOUiu0llj4OGrFoOKK3g2vey4/p2omKCoHrWtPxSwV3HA== + "@eslint/eslintrc@^1.3.0": version "1.3.0" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" @@ -372,12 +377,21 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^7.1.1: +acorn-node@^1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" + integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== + dependencies: + acorn "^7.0.0" + acorn-walk "^7.0.0" + xtend "^4.0.2" + +acorn-walk@^7.0.0, acorn-walk@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn@^7.1.1: +acorn@^7.0.0, acorn@^7.1.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -423,6 +437,19 @@ ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -443,6 +470,18 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +autoprefixer@^10.4.7: + version "10.4.7" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.7.tgz#1db8d195f41a52ca5069b7593be167618edbbedf" + integrity sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA== + dependencies: + browserslist "^4.20.3" + caniuse-lite "^1.0.30001335" + fraction.js "^4.2.0" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + axios@^0.27.2: version "0.27.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" @@ -456,6 +495,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -469,7 +513,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.2: +braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -481,6 +525,16 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== +browserslist@^4.20.3: + version "4.21.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.1.tgz#c9b9b0a54c7607e8dc3e01a0d311727188011a00" + integrity sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ== + dependencies: + caniuse-lite "^1.0.30001359" + electron-to-chromium "^1.4.172" + node-releases "^2.0.5" + update-browserslist-db "^1.0.4" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -494,6 +548,16 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +caniuse-lite@^1.0.30001335, caniuse-lite@^1.0.30001359: + version "1.0.30001361" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001361.tgz#ba2adb2527566fb96f3ac7c67698ae7fc495a28d" + integrity sha512-ybhCrjNtkFji1/Wto6SSJKkWk6kZgVQsDq5QI83SafsF6FXv2JB4df9eEdH6g8sdGgqTXrFLjAxqBGgYoU3azQ== + chai@^4.3.6: version "4.3.6" resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.6.tgz#ffe4ba2d9fa9d6680cc0b370adae709ec9011e9c" @@ -529,6 +593,21 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -548,7 +627,7 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@~1.1.4: +color-name@^1.1.4, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -653,11 +732,30 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + integrity sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +detective@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" + integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw== + dependencies: + acorn-node "^1.8.2" + defined "^1.0.0" + minimist "^1.2.6" + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" + integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -665,6 +763,11 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" + integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== + doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -679,6 +782,11 @@ domexception@^4.0.0: dependencies: webidl-conversions "^7.0.0" +electron-to-chromium@^1.4.172: + version "1.4.173" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.173.tgz#48f128dda49cd7f6317e65ac0085bd3a6b9b6e3b" + integrity sha512-Qo3LnVW6JRNhD32viSdPebxKI7K+3WeBDjU1+Q2yZS83zAh8C2LyPpzTimlciv6U74KpY9n/0ESAhUByRke0jw== + entities@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.0.tgz#62915f08d67353bb4eb67e3d62641a4059aec656" @@ -855,6 +963,11 @@ esbuild@^0.14.27: esbuild-windows-64 "0.14.47" esbuild-windows-arm64 "0.14.47" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -1034,7 +1147,7 @@ fast-diff@^1.1.2: resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.2.9: +fast-glob@^3.2.11, fast-glob@^3.2.9: version "3.2.11" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== @@ -1103,6 +1216,11 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +fraction.js@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" + integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1160,14 +1278,14 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" -glob-parent@^5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" -glob-parent@^6.0.1: +glob-parent@^6.0.1, glob-parent@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== @@ -1292,6 +1410,11 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +immutable@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" + integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -1339,6 +1462,13 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-boolean-object@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" @@ -1371,7 +1501,7 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -1512,6 +1642,11 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lilconfig@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25" + integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg== + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -1595,6 +1730,11 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -1615,6 +1755,11 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-releases@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" + integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== + normalize-package-data@^2.3.2: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -1625,6 +1770,16 @@ normalize-package-data@^2.3.2: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + npm-run-all@^4.1.5: version "4.1.5" resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" @@ -1652,6 +1807,11 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.1.tgz#10a9f268fbf4c461249ebcfe38e359aa36e2577c" integrity sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg== +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + object-inspect@^1.12.0, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" @@ -1767,7 +1927,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -1777,6 +1937,11 @@ pidtree@^0.3.0: resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -1790,7 +1955,46 @@ pinia@^2.0.14: "@vue/devtools-api" "^6.1.4" vue-demi "*" -postcss-selector-parser@^6.0.9: +postcss-import@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" + integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-js@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.0.tgz#31db79889531b80dc7bc9b0ad283e418dce0ac00" + integrity sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ== + dependencies: + camelcase-css "^2.0.1" + +postcss-load-config@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" + integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== + dependencies: + lilconfig "^2.0.5" + yaml "^1.10.2" + +postcss-nested@5.0.6: + version "5.0.6" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.6.tgz#466343f7fc8d3d46af3e7dba3fcd47d052a945bc" + integrity sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA== + dependencies: + postcss-selector-parser "^6.0.6" + +postcss-nesting@^10.1.9: + version "10.1.9" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-10.1.9.tgz#2aef4e68f222857dc36c0f77a69bf4a900a7e304" + integrity sha512-WlnqQecNMT7eizBpWwAnQOIk7Zr0A+OZJccEwQoTwmcIsZCVdcjT1LjXj1hBk6zR3BDLZQYsb5KZj2HquZgvTw== + dependencies: + "@csstools/selector-specificity" "^2.0.0" + postcss-selector-parser "^6.0.10" + +postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9: version "6.0.10" resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== @@ -1798,7 +2002,12 @@ postcss-selector-parser@^6.0.9: cssesc "^3.0.0" util-deprecate "^1.0.2" -postcss@^8.1.10, postcss@^8.4.13: +postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.1.10, postcss@^8.4.13, postcss@^8.4.14: version "8.4.14" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== @@ -1844,6 +2053,18 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-lru@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" + integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== + dependencies: + pify "^2.3.0" + read-pkg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" @@ -1853,6 +2074,13 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" @@ -1872,7 +2100,7 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== -resolve@^1.10.0, resolve@^1.22.0: +resolve@^1.1.7, resolve@^1.10.0, resolve@^1.22.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -1912,6 +2140,15 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sass@^1.53.0: + version "1.53.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.53.0.tgz#eab73a7baac045cc57ddc1d1ff501ad2659952eb" + integrity sha512-zb/oMirbKhUgRQ0/GFz8TSAwRq2IlR29vOUJZOx0l8sV+CkHUfHa4u5nqrG+1VceZp7Jfj59SVW9ogdhTvJDcQ== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + saxes@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" @@ -1974,7 +2211,7 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -source-map-js@^1.0.2: +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== @@ -2083,6 +2320,34 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +tailwindcss@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.4.tgz#64b09059805505902139fa805d97046080bd90b9" + integrity sha512-NrxbFV4tYsga/hpWbRyUfIaBrNMXDxx5BsHgBS4v5tlyjf+sDsgBg5m9OxjrXIqAS/uR9kicxLKP+bEHI7BSeQ== + dependencies: + arg "^5.0.2" + chokidar "^3.5.3" + color-name "^1.1.4" + detective "^5.2.1" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.2.11" + glob-parent "^6.0.2" + is-glob "^4.0.3" + lilconfig "^2.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.14" + postcss-import "^14.1.0" + postcss-js "^4.0.0" + postcss-load-config "^3.1.4" + postcss-nested "5.0.6" + postcss-selector-parser "^6.0.10" + postcss-value-parser "^4.2.0" + quick-lru "^5.1.1" + resolve "^1.22.0" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -2177,6 +2442,14 @@ universalify@^0.1.2: resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +update-browserslist-db@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz#dbfc5a789caa26b1db8990796c2c8ebbce304824" + integrity sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -2354,7 +2627,17 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xtend@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== From c9fa2995127774a59087db4d72e4e6208233b6c0 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Thu, 30 Jun 2022 01:02:47 +1000 Subject: [PATCH 04/46] Minor theme improvements --- traq-ui/css/buttons.css | 37 ++++++++ traq-ui/css/content.css | 26 ++++++ traq-ui/css/header.css | 46 ++++++++++ traq-ui/css/main.css | 3 + traq-ui/main.ts | 4 + vendor/traq/helpers/ui.php | 17 +++- vendor/traq/views/default/css/default.css | 10 +-- vendor/traq/views/default/css/default.css.map | 1 + vendor/traq/views/default/layouts/_head.phtml | 2 + .../views/default/layouts/_meta_nav.phtml | 44 +++++----- .../traq/views/default/layouts/default.phtml | 85 ++++++++++--------- vendor/traq/views/default/tickets/index.phtml | 2 +- 12 files changed, 208 insertions(+), 69 deletions(-) create mode 100644 traq-ui/css/buttons.css create mode 100644 traq-ui/css/content.css create mode 100644 traq-ui/css/header.css create mode 100644 traq-ui/css/main.css create mode 100644 traq-ui/main.ts create mode 100644 vendor/traq/views/default/css/default.css.map diff --git a/traq-ui/css/buttons.css b/traq-ui/css/buttons.css new file mode 100644 index 000000000..6cf1a5ae9 --- /dev/null +++ b/traq-ui/css/buttons.css @@ -0,0 +1,37 @@ +button, +input[type="submit"], +input[type="button"], +input[type="reset"] { + @apply py-2 px-3; + @apply border-0 rounded; + @apply bg-gray-200 hover:bg-gray-300 active:bg-gray-400; + @apply focus:ring-2 ring-gray-500; + @apply transition-all; + + &.primary { + @apply text-white; + @apply bg-brand-400; + @apply ring-brand-200; + + &:hover { + @apply bg-brand-500; + } + + &:active { + @apply bg-brand-600; + } + } + + &.success { + @apply bg-emerald-400; + @apply ring-emerald-200; + + &:hover { + @apply bg-emerald-500; + } + + &:active { + @apply bg-emerald-600; + } + } +} diff --git a/traq-ui/css/content.css b/traq-ui/css/content.css new file mode 100644 index 000000000..1c66fd05f --- /dev/null +++ b/traq-ui/css/content.css @@ -0,0 +1,26 @@ +body { + /* background: #e9e9e9; */ + @apply bg-gray-50; + @apply text-white; +} + +#page_title { + line-height: 1; + @apply my-2; +} + +#page { + @apply bg-white; + @apply text-black; + @apply shadow-sm; +} + +.content { + padding: 10px; +} + +#footer { + @apply mt-5; + @apply text-gray-400; + @apply text-center; +} diff --git a/traq-ui/css/header.css b/traq-ui/css/header.css new file mode 100644 index 000000000..ee84d425e --- /dev/null +++ b/traq-ui/css/header.css @@ -0,0 +1,46 @@ +#header, +#nav { + /* background: #3478aa; */ + @apply bg-brand-500; + @apply text-white; +} + +#meta_nav { + /* background: #265f98; */ + @apply bg-brand-600; + + @apply text-xs; + @apply h-5; + padding: 2px 5px; + + @apply flex; + @apply items-center; + + & ul { + margin: 0; + padding: 0; + + & li { + display: inline; + margin-right: 5px; + + &:last-child { + margin-right: 0; + } + } + } + + & a { + color: #fff; + text-decoration: none; + } + + & li.active, + & a:hover { + text-decoration: underline; + } +} + +#user_nav { + float: right; +} diff --git a/traq-ui/css/main.css b/traq-ui/css/main.css new file mode 100644 index 000000000..b5c61c956 --- /dev/null +++ b/traq-ui/css/main.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/traq-ui/main.ts b/traq-ui/main.ts new file mode 100644 index 000000000..0e55116d2 --- /dev/null +++ b/traq-ui/main.ts @@ -0,0 +1,4 @@ +import "./css/main.css" +import "./css/header.css" +import "./css/content.css" +import "./css/buttons.css" diff --git a/vendor/traq/helpers/ui.php b/vendor/traq/helpers/ui.php index 25fff61d3..aef0b3003 100644 --- a/vendor/traq/helpers/ui.php +++ b/vendor/traq/helpers/ui.php @@ -32,10 +32,21 @@ function ui_package($entry) { $manifestPath = dirname(dirname(dirname(__DIR__))).'/assets/ui/manifest.json'; $manifest = json_decode(file_get_contents($manifestPath), true); - $index = "traq-ui/${entry}/${entry}.ts"; + $index = "traq-ui/${entry}.ts"; if (isset($manifest[$index])) { - $file = $manifest[$index]['file']; + $info = $manifest[$index]; + $file = $info['file']; - return HTML::js_inc(Request::base()."assets/ui/${file}"); + $html = []; + + if (isset($info['css'])) { + foreach ($info['css'] as $cssFile) { + $html[] = HTML::css_link(Request::base()."assets/ui/${cssFile}"); + } + } + + $html[] = HTML::js_inc(Request::base()."assets/ui/${file}"); + + return implode(PHP_EOL, $html); } } \ No newline at end of file diff --git a/vendor/traq/views/default/css/default.css b/vendor/traq/views/default/css/default.css index 7beaf7f39..583ed20fb 100644 --- a/vendor/traq/views/default/css/default.css +++ b/vendor/traq/views/default/css/default.css @@ -1,7 +1,7 @@ /*! * Traq - * Copyright (C) 2009-2014 Jack Polgar - * Copyright (C) 2012-2014 Traq.io + * Copyright (C) 2009-2022 Jack Polgar + * Copyright (C) 2012-2022 Traq.io * https://github.com/nirix * http://traq.io * @@ -18,7 +18,7 @@ * * You should have received a copy of the GNU General Public License * along with Traq. If not, see . - */body{background:#e9e9e9;color:#fff}hr{margin-bottom:0}p{margin-bottom:10px}p:last-child{margin-bottom:0}h2{margin-bottom:0}h3{margin-bottom:5px}#page_title{line-height:1;margin-top:0}#header{background:#3478AA;color:#fff;padding:10px}#header h1{display:inline-block;color:#fff;margin:0}#header h1 a{color:#fff;text-decoration:none}#header #project_switcher_btn{display:none;position:absolute;margin-top:14px;margin-left:5px;height:10px;width:10px}#header #project_switcher_btn .arrow{display:inline-block;background:#fff;padding:5px;border-radius:4px}#header #project_switcher_btn .arrow em{display:block;border-left:3px solid transparent;border-right:3px solid transparent;border-top:5px solid #3478AA}#header .project_switcher{margin-top:12px;margin-left:2px}#search{float:right}#page{color:#000;background:#fff}.content{padding:10px}.box,.inline_box{background:#F6F6F6;border:1px solid #e4e4e4;padding:5px;margin-bottom:0}.box .box_title,.inline_box .box_title{margin-top:5px;margin-bottom:0}.tabular.section.box{margin-bottom:10px}.tabular.section.box:last-child{margin-bottom:0}.page_actions{float:right}.pagination .prev_link{float:left}.pagination .next_link{float:right}#footer{color:#000;padding:5px}code{background-color:#f8f8f8;border:1px solid #ccc;border-radius:3px;padding:2px}pre{background-color:#f8f8f8;border:1px solid #ccc;border-radius:3px;padding:2px;white-space:pre-wrap}pre code{background-color:transparent;border:0;border-radius:0;padding:0}.permissions.list .permission_section{background:#265F98;color:#fff}.permissions.list .permission_section:nth-child(even) td{background:inherit}.usercp.content #options,.usercp.content #info{padding:5px;width:48%}.usercp.content #info{float:left}.usercp.content #options{float:right}.changelog.content #changeset{margin-top:10px}.changelog.content #changeset ul{list-style:none;padding-left:5px}.changelog.content #changeset ul li:before{margin-right:8px;width:5px;text-align:center;display:inline-block}#nav{background:#3478AA;color:#000;height:20px;padding:0 5px}#nav ul{margin:0;padding:0;list-style:none}#nav li{display:inline}#nav a{color:#fff;padding:4px 10px;text-decoration:none}#nav a:hover,#nav a:focus,#nav a:active,#nav li.active a{background:#fff;color:#000}#meta_nav{font-size:11px;height:16px;padding:2px 5px;background:#265f98}#meta_nav ul{margin:0;padding:0}#meta_nav ul li{display:inline;margin-right:5px}#meta_nav ul li:last-child{margin-right:0}#meta_nav a{color:#fff;text-decoration:none}#meta_nav li.active,#meta_nav a:hover{text-decoration:underline}#user_nav{float:right}h3.list_title{font-size:15px}.list_title{margin-bottom:0}.list th.fixed_username,.list th.fixed_name{width:200px}.list th.actions,.list th.fixed_repo_slug{width:140px}table.list{margin:0}thead th{background:#3478AA;color:#fff}thead th a{color:#fff}thead th:hover,thead th a:hover{color:#fff}.tabs{height:24px;position:relative;overflow:hidden}.tabs ul{margin:0;position:absolute;padding-left:10px;width:2000px;border-bottom:1px solid #bbb;list-style:none}.tabs ul li{float:left;white-space:nowrap;margin-right:4px;position:relative;margin-bottom:-1px}.tabs ul li a{display:block;text-decoration:none;border:1px solid transparent;padding:2px 10px;border-radius:2px 2px 0 0;color:#8c8c8c;font-weight:bold}.tabs ul li a:hover{background:#f6f6f6}.tabs ul li.active a,.tabs ul li a:hover{border:1px solid #ccc}.tabs ul li.active a{background:#fff}.tabs ul li.active a{color:#000;border-bottom-color:#fff}.ui-autocomplete{position:absolute;cursor:default;border:1px solid #3478AA;background:#fff;box-shadow:0 2px 5px 1px rgba(0,0,0,0.5)}.ui-menu{list-style:none;padding:0px;margin:0;display:block;float:left}.ui-menu .ui-menu{margin-top:-3px}.ui-menu .ui-menu-item{margin:0;padding:0;float:left;clear:left;width:100%}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:4px;line-height:1;color:#000}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{background:#eee}.ui-helper-hidden-accessible{display:none}.ui-datepicker{position:absolute;border:1px solid #3478AA;background:#fff;box-shadow:0 2px 5px 1px rgba(0,0,0,0.5)}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{cursor:pointer;padding:0 0 10px 25px;background-position:center;background-repeat:no-repeat;text-indent:-9000px}.ui-datepicker .ui-datepicker-prev{float:left;margin-left:2px;background-image:url(":baseuri:assets/images/arrow_left.png")}.ui-datepicker .ui-datepicker-next{float:right;margin-right:2px;background-image:url(":baseuri:assets/images/arrow_right.png")}.ui-datepicker .ui-datepicker-title{text-align:center;color:#000}.ui-datepicker .ui-datepicker-calendar{margin:0}.ui-datepicker .ui-datepicker-calendar td{text-align:center;padding:3px}.ui-datepicker .ui-datepicker-current-day{background:#3478aa}.ui-datepicker .ui-datepicker-current-day a{color:#fff}/*! + */hr{margin-bottom:0}p{margin-bottom:10px}p:last-child{margin-bottom:0}h2{margin-bottom:0}h3{margin-bottom:5px}#header{color:#fff;padding:10px}#header h1{display:inline-block;color:#fff;margin:0}#header h1 a{color:#fff;text-decoration:none}#header #project_switcher_btn{display:none;position:absolute;margin-top:14px;margin-left:5px;height:10px;width:10px}#header #project_switcher_btn .arrow{display:inline-block;background:#fff;padding:5px;border-radius:4px}#header #project_switcher_btn .arrow em{display:block;border-left:3px solid rgba(0,0,0,0);border-right:3px solid rgba(0,0,0,0);border-top:5px solid #3478aa}#header .project_switcher{margin-top:12px;margin-left:2px}#search{float:right}.box,.inline_box{background:#f6f6f6;border:1px solid #e4e4e4;padding:5px;margin-bottom:0}.box .box_title,.inline_box .box_title{margin-top:5px;margin-bottom:0}.tabular.section.box{margin-bottom:10px}.tabular.section.box:last-child{margin-bottom:0}.page_actions{float:right}.pagination .prev_link{float:left}.pagination .next_link{float:right}#footer{color:#000;padding:5px}code{background-color:#f8f8f8;border:1px solid #ccc;border-radius:3px;padding:2px}pre{background-color:#f8f8f8;border:1px solid #ccc;border-radius:3px;padding:2px;white-space:pre-wrap}pre code{background-color:rgba(0,0,0,0);border:0;border-radius:0;padding:0}.permissions.list .permission_section{background:#265f98;color:#fff}.permissions.list .permission_section:nth-child(even) td{background:inherit}.usercp.content #options,.usercp.content #info{padding:5px;width:48%}.usercp.content #info{float:left}.usercp.content #options{float:right}.changelog.content #changeset{margin-top:10px}.changelog.content #changeset ul{list-style:none;padding-left:5px}.changelog.content #changeset ul li:before{margin-right:8px;width:5px;text-align:center;display:inline-block}#nav{height:20px;padding:0 5px}#nav ul{margin:0;padding:0;list-style:none}#nav li{display:inline}#nav a{color:#fff;padding:4px 10px;text-decoration:none}#nav a:hover,#nav a:focus,#nav a:active,#nav li.active a{background:#fff;color:#000}h3.list_title{font-size:15px}.list_title{margin-bottom:0}.list th.fixed_username,.list th.fixed_name{width:200px}.list th.actions,.list th.fixed_repo_slug{width:140px}table.list{margin:0}thead th{background:#3478aa;color:#fff}thead th a{color:#fff}thead th:hover,thead th a:hover{color:#fff}.tabs{height:24px;position:relative;overflow:hidden}.tabs ul{margin:0;position:absolute;padding-left:10px;width:2000px;border-bottom:1px solid #bbb;list-style:none}.tabs ul li{float:left;white-space:nowrap;margin-right:4px;position:relative;margin-bottom:-1px}.tabs ul li a{display:block;text-decoration:none;border:1px solid rgba(0,0,0,0);padding:2px 10px;border-radius:2px 2px 0 0;color:#8c8c8c;font-weight:bold}.tabs ul li a:hover{background:#f6f6f6}.tabs ul li.active a,.tabs ul li a:hover{border:1px solid #ccc}.tabs ul li.active a{background:#fff}.tabs ul li.active a{color:#000;border-bottom-color:#fff}.ui-autocomplete{position:absolute;cursor:default;border:1px solid #3478aa;background:#fff;box-shadow:0 2px 5px 1px rgba(0,0,0,.5)}.ui-menu{list-style:none;padding:0px;margin:0;display:block;float:left}.ui-menu .ui-menu{margin-top:-3px}.ui-menu .ui-menu-item{margin:0;padding:0;float:left;clear:left;width:100%}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:4px;line-height:1;color:#000}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{background:#eee}.ui-helper-hidden-accessible{display:none}.ui-datepicker{position:absolute;border:1px solid #3478aa;background:#fff;box-shadow:0 2px 5px 1px rgba(0,0,0,.5)}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{cursor:pointer;padding:0 0 10px 25px;background-position:center;background-repeat:no-repeat;text-indent:-9000px}.ui-datepicker .ui-datepicker-prev{float:left;margin-left:2px;background-image:url(":baseuri:assets/images/arrow_left.png")}.ui-datepicker .ui-datepicker-next{float:right;margin-right:2px;background-image:url(":baseuri:assets/images/arrow_right.png")}.ui-datepicker .ui-datepicker-title{text-align:center;color:#000}.ui-datepicker .ui-datepicker-calendar{margin:0}.ui-datepicker .ui-datepicker-calendar td{text-align:center;padding:3px}.ui-datepicker .ui-datepicker-current-day{background:#3478aa}.ui-datepicker .ui-datepicker-current-day a{color:#fff}/*! * Like a Boss * Copyright (c) 2012 Jack P. * All Rights Reserved @@ -30,9 +30,9 @@ * Copyright (c) 2012 Jack P. * All Rights Reserved * https://github.com/nirix - */.popover{display:none;position:absolute;border-radius:4px;box-shadow:0 0 5px #444;z-index:2100;background:#fff;color:#000;padding:0;margin-top:5px}.popover .popover-content{padding:4px}.popover .popover-footer{background:#f4f4f4;padding:2px;text-align:center;border-radius:0 0 4px 4px}.popover ul,.popover ol{margin-bottom:0}.popover:before,.popover:after{bottom:100%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.popover:before{border-bottom-color:#444;opacity:0.3;border-width:6px;left:50%;margin-left:-6px}.popover:after{border-bottom-color:#fff;border-width:5px;left:50%;margin-left:-5px}#overlay{display:none;position:fixed;border-radius:4px;box-shadow:0 0 5px #444;z-index:2100;background:#fff;color:#000;padding:0}#overlay .content{padding:10px}#overlay h3{background:#3478AA;color:#fff;padding:4px 6px;border-radius:3px 3px 0 0;margin:0}#overlay .box{border:0;background:transparent;margin:0 5px}/*! + */.popover{display:none;position:absolute;border-radius:4px;box-shadow:0 0 5px #444;z-index:2100;background:#fff;color:#000;padding:0;margin-top:5px}.popover .popover-content{padding:4px}.popover .popover-footer{background:#f4f4f4;padding:2px;text-align:center;border-radius:0 0 4px 4px}.popover ul,.popover ol{margin-bottom:0}.popover:before,.popover:after{bottom:100%;border:solid rgba(0,0,0,0);content:" ";height:0;width:0;position:absolute;pointer-events:none}.popover:before{border-bottom-color:#444;opacity:.3;border-width:6px;left:50%;margin-left:-6px}.popover:after{border-bottom-color:#fff;border-width:5px;left:50%;margin-left:-5px}#overlay{display:none;position:fixed;border-radius:4px;box-shadow:0 0 5px #444;z-index:2100;background:#fff;color:#000;padding:0}#overlay .content{padding:10px}#overlay h3{background:#3478aa;color:#fff;padding:4px 6px;border-radius:3px 3px 0 0;margin:0}#overlay .box{border:0;background:rgba(0,0,0,0);margin:0 5px}/*! * SexyTooltips * Copyright (c) 2012 Jack P. * All Rights Reserved * https://github.com/nirix - */#sexytooltip{display:none;position:absolute;border-radius:4px;z-index:2100;background:rgba(0,0,0,0.7);color:#fff;padding:4px;font-size:11px;max-width:300px}#sexytooltip code{background:#555;border-color:#a3a3a3}#sexytooltip.sexytooltip-right:before,#sexytooltip.sexytooltip-right:after{right:100%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}#sexytooltip.sexytooltip-right:before{border-right-color:rgba(0,0,0,0.7);border-width:6px;top:50%;margin-top:-6px}#sexytooltip.sexytooltip-top:before,#sexytooltip.sexytooltip-top:after{top:100%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}#sexytooltip.sexytooltip-top:before{border-top-color:rgba(0,0,0,0.7);border-width:6px;left:10px;margin-left:-6px}.admin.content h3{margin-bottom:0;line-height:1}.admin.content dl{margin:0}.admin.content dl:after{display:table;content:"";line-height:0;clear:both}.admin.content dl dt{float:left;clear:both}.admin.content dl dd{float:right}.admin.content #update{text-align:center}#traq_news{width:100%;margin-top:10px}#traq_news ul{margin:0;padding:0;list-style:none}#traq_news ul h4{margin:0;font-weight:bold;font-size:16px;display:inline-block;margin-right:10px}#traq_news .secure_alert{display:none}.button_new,.button_edit,.button_delete,.button_move,.btn_plugin_enable,.btn_plugin_disable,.btn_plugin_install,.btn_plugin_uninstall,.button_bug_new,.button_previous,.button_revisions{padding:1px 0 1px 20px;background-position:left center;background-repeat:no-repeat}.button_new.small,.button_edit.small,.button_delete.small,.button_move.small,.btn_plugin_enable.small,.btn_plugin_disable.small,.btn_plugin_install.small,.btn_plugin_uninstall.small,.button_bug_new.small,.button_previous.small,.button_revisions.small{background-position:center;padding:0;height:18px;width:18px;text-indent:-5000px;display:inline-block;margin:0}.button_next{padding:1px 20px 1px 0;background-position:right center;background-repeat:no-repeat}.button_new{background-image:url(":baseuri:assets/images/add.png")}.button_edit{background-image:url(":baseuri:assets/images/pencil.png")}.button_delete{background-image:url(":baseuri:assets/images/delete.png")}.btn_plugin_enable{background-image:url(":baseuri:assets/images/plug-connect.png")}.btn_plugin_disable{background-image:url(":baseuri:assets/images/plug-disconnect.png")}.btn_plugin_install{background-image:url(":baseuri:assets/images/plug-plus.png")}.btn_plugin_uninstall{background-image:url(":baseuri:assets/images/plug-minus.png")}.button_bug_new{background-image:url(":baseuri:assets/images/bug_add.png")}.button_move{background-image:url(":baseuri:assets/images/arrow_left.png")}.button_next{background-image:url(":baseuri:assets/images/arrow_right.png")}.button_previous{background-image:url(":baseuri:assets/images/arrow_left.png")}.button_revisions{background-image:url(":baseuri:assets/images/page_white_stack.png")}button.button_new,button.button_delete,.icon_only.button_new,.icon_only.button_delete{border:0;text-indent:-5000px;background-color:transparent;cursor:pointer}.icon_only span{display:none}.tabular .group{margin:0;padding:5px 0;padding-left:150px;clear:left}.tabular .group label:first-child{font-weight:bold;float:left;margin-left:-145px;text-align:right;width:135px}.tabular input{margin-top:0px;vertical-align:middle}.tabular select{margin-top:0}.usercp.content fieldset .tabular .group{padding-left:200px}.usercp.content fieldset .tabular .group label:first-child{margin-left:-200px;width:195px}form.horizontal .group{width:auto;padding:0 4px;display:table-cell;padding-right:15px}form.horizontal label{margin-right:5px}.error ul,.success ul,.notice ul,.info ul{margin-bottom:0}form .actions{text-align:center;padding:4px 0}form abbr{border-radius:15px;background:#d5edf8;color:#205791;border:1px solid #92cae4;padding:0 5px;cursor:help}form abbr.hint{background:transparent;border:0;border-bottom:1px dotted #666;padding:0;margin-left:4px;border-radius:0}form select{margin-top:0}form textarea{height:150px;width:98%}#register_form .box{margin-bottom:10px}input[type=text],input[type=password],input[type=email],input[type=url],textarea{padding:2px;font-size:12px}input[type=text]:focus,input[type=password]:focus,input[type=email]:focus,input[type=url]:focus,textarea:focus{border-color:#3478AA}input[type=text].error,input[type=password].error,input[type=email].error,input[type=url].error,textarea.error{color:#8a1f11;border:1px solid red;background-color:#fff;padding:2px}input[type=text],input[type=password],input[type=email],input[type=url]{width:200px}.overlay_thin input[type=text],.overlay_thin input[type=password],.overlay_thin input[type=email],.overlay_thin input[type=url]{width:120px}.tabular .summary input{width:300px}.tabular .properties .field{float:left;width:335px;min-height:27px}.tabular .properties .field button{margin:0;margin-bottom:0.5em}#overlay form{margin:0;width:600px}#overlay form textarea{width:95%}#overlay form.overlay_thin{width:100%;min-width:300px}#overlay form .actions,#overlay .overlay-actions{text-align:center;padding:4px 0;background-color:#f6f6f6;border-radius:0 0 4px 4px}#overlay #ticket_tasks_manager{padding:10px}#ticket_tasks_data{display:none}.popover_confirm{padding:5px;text-align:center}.progress_bar{width:400px;border:1px solid #d7d7d7;float:left;margin:2px 0;margin-right:5px}.progress_bar td{padding:0}.progress_bar .closed{background:#bae0ba}.progress_bar .open a,.progress_bar .closed a{display:block;padding:6px 0}.progress{margin-bottom:5px}.progress .percent_complete{margin:0 5px;font-size:12px;display:block}.progress .progress_info{font-style:italic}.progress .progress_info a{margin-right:5px;color:#aaa;text-decoration:none}.progress .progress_info a:hover{text-decoration:underline}.profile.content .span-16{width:665px}.profile.content .sidebar .box{margin-bottom:5px}.profile.content .sidebar .box:last-child{margin-bottom:0}.profile.content .sidebar .information dl{margin:0}.profile.content .sidebar .information dl dt{min-width:100px;text-align:right;margin:0;margin-right:2px;display:inline-block}.profile.content .sidebar .information dl dd{width:auto;min-width:50px;margin:0;display:inline-block}.profile.content #assigned_to table.list th.project{width:90px}.profile.content #assigned_to table.list th.owner{width:85px}.profile.content #assigned_to table.list th.status{width:75px}.profile.content #assigned_to table.list th.created,.profile.content #assigned_to table.list th.updated{width:90px}#project_list{margin:0;padding:0;list-style:none}#project_list li h3{margin:0;margin-top:10px}#project_list li h3 em{font-size:14px}#project_list li nav ul{list-style:none;padding:0;margin:0}#project_list li nav ul li{display:inline;margin-right:4px}.project_info{margin-top:10px}#project_wiki_nav{padding-top:5px}.roadmap #milestones{margin:0;padding:0;list-style:none;margin-top:15px}.roadmap #milestones li.milestone{margin-top:15px}.roadmap #milestones li.milestone:first-child{margin-top:0px}.roadmap #milestones li.milestone p{margin-bottom:5px}.roadmap #milestones h3{font-weight:bold}.roadmap #milestones h3 a{text-decoration:none}#ticket_filters,#ticket_columns{padding:0;margin:0}#ticket_filters legend,#ticket_columns legend{color:#999;margin:0 5px;padding:0 2px}#ticket_filters table,#ticket_columns table{margin:0}#ticket_filters table td,#ticket_columns table td{vertical-align:top}#ticket_filters table td.label,#ticket_columns table td.label{font-weight:bold;text-align:right;width:100px}#ticket_filters table td.condition,#ticket_columns table td.condition{width:120px}#ticket_filters table td.value .multiselect,#ticket_columns table td.value .multiselect{width:200px}#ticket_filters table td.ticket_filter_action,#ticket_columns table td.ticket_filter_action{text-align:right}#ticket_filters table #ticket_filter_actions,#ticket_columns table #ticket_filter_actions{text-align:right}#ticket_columns{margin-top:10px}#ticket_columns_content{padding:10px;display:none}#ticket_columns_content .actions{text-align:left;padding-top:5px;padding-bottom:0}#ticket_info{background:#ffffdd;border-bottom:2px solid #dddd99;padding:5px}#ticket_info .ticket_actions{float:right}#ticket_info .properties .property{width:227px;margin-right:10px;float:left;border-bottom:1px solid #dd9}#ticket_info .properties .property label{color:#663}#ticket_info .properties .property div.value,#ticket_info .properties .property span.value,#ticket_info .properties .property ul.value{float:right}#ticket_info .properties .property:nth-child(4n){margin-right:0px}#ticket_info .properties .property ul{display:inline-block;margin:0;padding:0;list-style-type:none}#ticket_info .properties .property ul li{display:inline}#ticket_info #description h3,#ticket_info #attachments h3,#ticket_info #tasks h3{margin-top:5px;display:block;font-weight:bold;color:#663;border-bottom:1px solid #dd9;padding-bottom:2px}#ticket_info #attachments ul,#ticket_info #tasks ul{margin:0}#ticket_info #tasks ul{list-style:none;padding:0}#ticket_info #tasks label{font-weight:normal}#ticket_summary{margin:0;font-size:20px}#ticket_info h3{border-bottom:1px solid #dddd99;color:#666633;margin:0;margin-bottom:5px;font-size:100%;font-weight:normal}#ticket_history{border-bottom:1px solid #3478AA;margin-bottom:5px}#ticket_history h3{margin-top:5px;padding:10px;padding-bottom:0}#ticket_history .update h4{background:#3478AA;color:#fff;padding:4px 15px;margin:0;margin-top:10px;margin-bottom:10px;font-weight:bold}#ticket_history .update h4 a{color:#fff}#ticket_history .update .changes{margin:4px 0px 4px 15px}#ticket_history .update .comment{padding:0 10px 0px 15px}#ticket_history .update .comment p:last-child{margin-bottom:0}#ticket_history .update:first-child h4{margin-top:0}#ticket_history .update:last-child{padding-bottom:10px}#ticket_history .ticket_history_to,#ticket_history .ticket_history_from{font-family:Courier, "Courier New", monospace;background:#F6F6F6;border:1px solid #e4e4e4;padding:0 2px}.attachment_filename{font-family:Courier, "Courier New", monospace}#popover .voters{list-style:none;padding-left:5px}#voters_list{width:500px;max-height:300px;overflow:scroll;padding:5px;margin:5px}#voters_list li{display:inline}table.ticket_listing th.mass_actions{width:18px}table.ticket_listing tr.priority_1 td{background-color:#fdc}table.ticket_listing tr.priority_1:nth-child(even) td{background-color:#fed}table.ticket_listing tr.priority_2 td{background-color:#ffb}table.ticket_listing tr.priority_2:nth-child(even) td{background-color:#ffd}table.ticket_listing tr.priority_3 td{background-color:#d9f9f9}table.ticket_listing tr.priority_3:nth-child(even) td{background-color:#dff}table.ticket_listing tr.priority_4 td{background-color:#e7eeff}table.ticket_listing tr.priority_4:nth-child(even) td{background-color:#dde7ff}table.ticket_listing tr.priority_5 td{background-color:#fbfbfb}table.ticket_listing tr.priority_5:nth-child(even) td{background-color:#f6f6f6}table.ticket_listing tr:hover td,table.ticket_listing tr:hover:nth-child(even) td{background:#fff}#mass_actions{display:none}.sort_indicator{float:right;position:relative;top:7px;left:-2px}.sort_indicator.asc,.sort_indicator.desc{position:relative;background:#ffffff}.sort_indicator.asc:after,.sort_indicator.desc:after{border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none;border-color:rgba(255,255,255,0);border-width:5px;left:50%;margin-left:-5px}.sort_indicator.asc{top:12px}.sort_indicator.asc:after{bottom:100%;border-bottom-color:#ffffff}.sort_indicator.desc:after{top:100%;border-top-color:#ffffff}.timeline.content h3{margin-top:15px}.timeline.content dl dt{font-weight:normal}.timeline.content dl dt .time{color:#777}.timeline.content .timeline_filters{width:180px;float:right}.timeline.content fieldset{padding:5px 10px;padding-bottom:0}.timeline.content fieldset .actions{margin-top:5px}.wiki.content #page_title{margin-bottom:10px}.wiki.content #head{height:28px;margin-bottom:10px}.wiki.content #head h2{float:left;margin-bottom:0}.wiki.content #head #wiki_actions{float:right;list-style:none;margin:0;padding:0}.wiki.content #head #wiki_actions li{margin-left:5px;display:inline}.wiki.content #head #wiki_actions a{font-weight:bold}.wiki.content ul#pages li a{text-decoration:none;font-size:14px;font-weight:bold} + */#sexytooltip{display:none;position:absolute;border-radius:4px;z-index:2100;background:rgba(0,0,0,.7);color:#fff;padding:4px;font-size:11px;max-width:300px}#sexytooltip code{background:#555;border-color:#a3a3a3}#sexytooltip.sexytooltip-right:before,#sexytooltip.sexytooltip-right:after{right:100%;border:solid rgba(0,0,0,0);content:" ";height:0;width:0;position:absolute;pointer-events:none}#sexytooltip.sexytooltip-right:before{border-right-color:rgba(0,0,0,.7);border-width:6px;top:50%;margin-top:-6px}#sexytooltip.sexytooltip-top:before,#sexytooltip.sexytooltip-top:after{top:100%;border:solid rgba(0,0,0,0);content:" ";height:0;width:0;position:absolute;pointer-events:none}#sexytooltip.sexytooltip-top:before{border-top-color:rgba(0,0,0,.7);border-width:6px;left:10px;margin-left:-6px}.admin.content h3{margin-bottom:0;line-height:1}.admin.content dl{margin:0}.admin.content dl:after{display:table;content:"";line-height:0;clear:both}.admin.content dl dt{float:left;clear:both}.admin.content dl dd{float:right}.admin.content #update{text-align:center}#traq_news{width:100%;margin-top:10px}#traq_news ul{margin:0;padding:0;list-style:none}#traq_news ul h4{margin:0;font-weight:bold;font-size:16px;display:inline-block;margin-right:10px}#traq_news .secure_alert{display:none}.button_new,.button_edit,.button_delete,.button_move,.btn_plugin_enable,.btn_plugin_disable,.btn_plugin_install,.btn_plugin_uninstall,.button_bug_new,.button_previous,.button_revisions{padding:1px 0 1px 20px;background-position:left center;background-repeat:no-repeat}.button_new.small,.button_edit.small,.button_delete.small,.button_move.small,.btn_plugin_enable.small,.btn_plugin_disable.small,.btn_plugin_install.small,.btn_plugin_uninstall.small,.button_bug_new.small,.button_previous.small,.button_revisions.small{background-position:center;padding:0;height:18px;width:18px;text-indent:-5000px;display:inline-block;margin:0}.button_next{padding:1px 20px 1px 0;background-position:right center;background-repeat:no-repeat}.button_new{background-image:url(":baseuri:assets/images/add.png")}.button_edit{background-image:url(":baseuri:assets/images/pencil.png")}.button_delete{background-image:url(":baseuri:assets/images/delete.png")}.btn_plugin_enable{background-image:url(":baseuri:assets/images/plug-connect.png")}.btn_plugin_disable{background-image:url(":baseuri:assets/images/plug-disconnect.png")}.btn_plugin_install{background-image:url(":baseuri:assets/images/plug-plus.png")}.btn_plugin_uninstall{background-image:url(":baseuri:assets/images/plug-minus.png")}.button_bug_new{background-image:url(":baseuri:assets/images/bug_add.png")}.button_move{background-image:url(":baseuri:assets/images/arrow_left.png")}.button_next{background-image:url(":baseuri:assets/images/arrow_right.png")}.button_previous{background-image:url(":baseuri:assets/images/arrow_left.png")}.button_revisions{background-image:url(":baseuri:assets/images/page_white_stack.png")}button.button_new,button.button_delete,.icon_only.button_new,.icon_only.button_delete{border:0;text-indent:-5000px;background-color:rgba(0,0,0,0);cursor:pointer}.icon_only span{display:none}.tabular .group{margin:0;padding:5px 0;padding-left:150px;clear:left}.tabular .group label:first-child{font-weight:bold;float:left;margin-left:-145px;text-align:right;width:135px}.tabular input{margin-top:0px;vertical-align:middle}.tabular select{margin-top:0}.usercp.content fieldset .tabular .group{padding-left:200px}.usercp.content fieldset .tabular .group label:first-child{margin-left:-200px;width:195px}form.horizontal .group{width:auto;padding:0 4px;display:table-cell;padding-right:15px}form.horizontal label{margin-right:5px}.error ul,.success ul,.notice ul,.info ul{margin-bottom:0}form .actions{text-align:center;padding:4px 0}form abbr{border-radius:15px;background:#d5edf8;color:#205791;border:1px solid #92cae4;padding:0 5px;cursor:help}form abbr.hint{background:rgba(0,0,0,0);border:0;border-bottom:1px dotted #666;padding:0;margin-left:4px;border-radius:0}form select{margin-top:0}form textarea{height:150px;width:98%}#register_form .box{margin-bottom:10px}input[type=text],input[type=password],input[type=email],input[type=url],textarea{padding:2px;font-size:12px}input[type=text]:focus,input[type=password]:focus,input[type=email]:focus,input[type=url]:focus,textarea:focus{border-color:#3478aa}input[type=text].error,input[type=password].error,input[type=email].error,input[type=url].error,textarea.error{color:#8a1f11;border:1px solid red;background-color:#fff;padding:2px}input[type=text],input[type=password],input[type=email],input[type=url]{width:200px}.overlay_thin input[type=text],.overlay_thin input[type=password],.overlay_thin input[type=email],.overlay_thin input[type=url]{width:120px}.tabular .summary input{width:300px}.tabular .properties .field{float:left;width:335px;min-height:27px}.tabular .properties .field button{margin:0;margin-bottom:.5em}#overlay form{margin:0;width:600px}#overlay form textarea{width:95%}#overlay form.overlay_thin{width:100%;min-width:300px}#overlay form .actions,#overlay .overlay-actions{text-align:center;padding:4px 0;background-color:#f6f6f6;border-radius:0 0 4px 4px}#overlay #ticket_tasks_manager{padding:10px}#ticket_tasks_data{display:none}.popover_confirm{padding:5px;text-align:center}.progress_bar{width:400px;border:1px solid #d7d7d7;float:left;margin:2px 0;margin-right:5px}.progress_bar td{padding:0}.progress_bar .closed{background:#bae0ba}.progress_bar .open a,.progress_bar .closed a{display:block;padding:6px 0}.progress{margin-bottom:5px}.progress .percent_complete{margin:0 5px;font-size:12px;display:block}.progress .progress_info{font-style:italic}.progress .progress_info a{margin-right:5px;color:#aaa;text-decoration:none}.progress .progress_info a:hover{text-decoration:underline}.profile.content .span-16{width:665px}.profile.content .sidebar .box{margin-bottom:5px}.profile.content .sidebar .box:last-child{margin-bottom:0}.profile.content .sidebar .information dl{margin:0}.profile.content .sidebar .information dl dt{min-width:100px;text-align:right;margin:0;margin-right:2px;display:inline-block}.profile.content .sidebar .information dl dd{width:auto;min-width:50px;margin:0;display:inline-block}.profile.content #assigned_to table.list th.project{width:90px}.profile.content #assigned_to table.list th.owner{width:85px}.profile.content #assigned_to table.list th.status{width:75px}.profile.content #assigned_to table.list th.created,.profile.content #assigned_to table.list th.updated{width:90px}#project_list{margin:0;padding:0;list-style:none}#project_list li h3{margin:0;margin-top:10px}#project_list li h3 em{font-size:14px}#project_list li nav ul{list-style:none;padding:0;margin:0}#project_list li nav ul li{display:inline;margin-right:4px}.project_info{margin-top:10px}#project_wiki_nav{padding-top:5px}.roadmap #milestones{margin:0;padding:0;list-style:none;margin-top:15px}.roadmap #milestones li.milestone{margin-top:15px}.roadmap #milestones li.milestone:first-child{margin-top:0px}.roadmap #milestones li.milestone p{margin-bottom:5px}.roadmap #milestones h3{font-weight:bold}.roadmap #milestones h3 a{text-decoration:none}#ticket_filters,#ticket_columns{padding:0;margin:0}#ticket_filters legend,#ticket_columns legend{color:#999;margin:0 5px;padding:0 2px}#ticket_filters table,#ticket_columns table{margin:0}#ticket_filters table td,#ticket_columns table td{vertical-align:top}#ticket_filters table td.label,#ticket_columns table td.label{font-weight:bold;text-align:right;width:100px}#ticket_filters table td.condition,#ticket_columns table td.condition{width:120px}#ticket_filters table td.value .multiselect,#ticket_columns table td.value .multiselect{width:200px}#ticket_filters table td.ticket_filter_action,#ticket_columns table td.ticket_filter_action{text-align:right}#ticket_filters table #ticket_filter_actions,#ticket_columns table #ticket_filter_actions{text-align:right}#ticket_columns{margin-top:10px}#ticket_columns_content{padding:10px;display:none}#ticket_columns_content .actions{text-align:left;padding-top:5px;padding-bottom:0}#ticket_info{background:#ffd;border-bottom:2px solid #dd9;padding:5px}#ticket_info .ticket_actions{float:right}#ticket_info .properties .property{width:227px;margin-right:10px;float:left;border-bottom:1px solid #dd9}#ticket_info .properties .property label{color:#663}#ticket_info .properties .property div.value,#ticket_info .properties .property span.value,#ticket_info .properties .property ul.value{float:right}#ticket_info .properties .property:nth-child(4n){margin-right:0px}#ticket_info .properties .property ul{display:inline-block;margin:0;padding:0;list-style-type:none}#ticket_info .properties .property ul li{display:inline}#ticket_info #description h3,#ticket_info #attachments h3,#ticket_info #tasks h3{margin-top:5px;display:block;font-weight:bold;color:#663;border-bottom:1px solid #dd9;padding-bottom:2px}#ticket_info #attachments ul,#ticket_info #tasks ul{margin:0}#ticket_info #tasks ul{list-style:none;padding:0}#ticket_info #tasks label{font-weight:normal}#ticket_summary{margin:0;font-size:20px}#ticket_info h3{border-bottom:1px solid #dd9;color:#663;margin:0;margin-bottom:5px;font-size:100%;font-weight:normal}#ticket_history{border-bottom:1px solid #3478aa;margin-bottom:5px}#ticket_history h3{margin-top:5px;padding:10px;padding-bottom:0}#ticket_history .update h4{background:#3478aa;color:#fff;padding:4px 15px;margin:0;margin-top:10px;margin-bottom:10px;font-weight:bold}#ticket_history .update h4 a{color:#fff}#ticket_history .update .changes{margin:4px 0px 4px 15px}#ticket_history .update .comment{padding:0 10px 0px 15px}#ticket_history .update .comment p:last-child{margin-bottom:0}#ticket_history .update:first-child h4{margin-top:0}#ticket_history .update:last-child{padding-bottom:10px}#ticket_history .ticket_history_to,#ticket_history .ticket_history_from{font-family:Courier,"Courier New",monospace;background:#f6f6f6;border:1px solid #e4e4e4;padding:0 2px}.attachment_filename{font-family:Courier,"Courier New",monospace}#popover .voters{list-style:none;padding-left:5px}#voters_list{width:500px;max-height:300px;overflow:scroll;padding:5px;margin:5px}#voters_list li{display:inline}table.ticket_listing th.mass_actions{width:18px}#mass_actions{display:none}.sort_indicator{float:right;position:relative;top:7px;left:-2px}.sort_indicator.asc,.sort_indicator.desc{position:relative;background:#fff}.sort_indicator.asc:after,.sort_indicator.desc:after{border:solid rgba(0,0,0,0);content:" ";height:0;width:0;position:absolute;pointer-events:none;border-color:rgba(255,255,255,0);border-width:5px;left:50%;margin-left:-5px}.sort_indicator.asc{top:12px}.sort_indicator.asc:after{bottom:100%;border-bottom-color:#fff}.sort_indicator.desc:after{top:100%;border-top-color:#fff}.timeline.content h3{margin-top:15px}.timeline.content dl dt{font-weight:normal}.timeline.content dl dt .time{color:#777}.timeline.content .timeline_filters{width:180px;float:right}.timeline.content fieldset{padding:5px 10px;padding-bottom:0}.timeline.content fieldset .actions{margin-top:5px}.wiki.content #page_title{margin-bottom:10px}.wiki.content #head{height:28px;margin-bottom:10px}.wiki.content #head h2{float:left;margin-bottom:0}.wiki.content #head #wiki_actions{float:right;list-style:none;margin:0;padding:0}.wiki.content #head #wiki_actions li{margin-left:5px;display:inline}.wiki.content #head #wiki_actions a{font-weight:bold}.wiki.content ul#pages li a{text-decoration:none;font-size:14px;font-weight:bold}/*# sourceMappingURL=default.css.map */ diff --git a/vendor/traq/views/default/css/default.css.map b/vendor/traq/views/default/css/default.css.map new file mode 100644 index 000000000..dc0897e47 --- /dev/null +++ b/vendor/traq/views/default/css/default.css.map @@ -0,0 +1 @@ +{"version":3,"sourceRoot":"","sources":["../../../../../_dev/css/default.scss","../../../../../_dev/css/_nav.scss","../../../../../_dev/css/_tables.scss","../../../../../_dev/css/_tabs.scss","../../../../../_dev/css/_jquery.scss","../../../../../_dev/css/_likeaboss.scss","../../../../../_dev/css/_popover.scss","../../../../../_dev/css/_overlay.scss","../../../../../_dev/css/_sexytooltips.scss","../../../../../_dev/css/_admin.scss","../../../../../_dev/css/_buttons.scss","../../../../../_dev/css/_forms.scss","../../../../../_dev/css/_milestones.scss","../../../../../_dev/css/_profiles.scss","../../../../../_dev/css/_projects.scss","../../../../../_dev/css/_roadmap.scss","../../../../../_dev/css/_ticket_filters.scss","../../../../../_dev/css/_tickets.scss","../../../../../_dev/css/_timeline.scss","../../../../../_dev/css/_wiki.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAwBA,GACI,gBAGJ,EACI,mBAEA,aACI,gBAMR,GACI,gBAGJ,GACI,kBAMJ,QAEI,WACA,aAEA,WACI,qBACA,WACA,SAEA,aACI,WACA,qBAIR,8BACI,aACA,kBACA,gBACA,gBACA,YACA,WAEA,qCACI,qBACA,gBACA,YACA,kBAEA,wCACI,cACA,oCACA,qCACA,6BAKZ,0BACI,gBACA,gBAIR,QACI,YAKJ,iBAEI,mBACA,yBACA,YACA,gBAEA,uCACI,eACA,gBAIR,qBACI,mBAEA,gCACI,gBAIR,cACI,YAIA,uBACI,WAGJ,uBACI,YAMR,QACI,WACA,YAKJ,KACI,yBACA,sBACA,kBACA,YAGJ,IACI,yBACA,sBACA,kBACA,YACA,qBAEA,SACI,+BACA,SACA,gBACA,UAOJ,sCACI,mBACA,WAEA,yDACI,mBAQR,+CACI,YACA,UAGJ,sBACI,WAGJ,yBACI,YAOJ,8BACI,gBAEA,iCACI,gBACA,iBAGI,2CACI,iBACA,UACA,kBACA,qBCjNpB,KAGI,YACA,cAEA,QACI,SACA,UACA,gBAGJ,QACI,eAGJ,OACI,WACA,iBACA,qBAGJ,yDACI,gBACA,WCxBR,cACI,eAGJ,YACI,gBAKI,4CACI,YAGJ,0CACI,YAKZ,WACI,SAGJ,SACI,mBACA,WAEA,WACI,WAGJ,gCACI,WCjCR,MACI,YACA,kBACA,gBAEA,SACI,SACA,kBACA,kBACA,aACA,6BACA,gBAEA,YACI,WACA,mBACA,iBACA,kBACA,mBAEA,cACI,cACA,qBACA,+BACA,iBACA,0BACA,cACA,iBAEA,oBACI,mBAIR,yCACI,sBAGJ,qBACI,gBAGJ,qBACI,WACA,yBC5ChB,iBACI,kBACA,eACA,yBACA,gBACA,wCAGJ,SACI,gBACA,YACA,SACA,cACA,WAEA,kBACI,gBAGJ,uBACI,SACA,UACA,WACA,WACA,WAEA,yBACI,qBACA,cACA,YACA,cACA,WAEA,iFAEI,gBAMhB,6BACI,aAGJ,eACI,kBACA,yBACA,gBACA,wCAEA,sEAEI,eACA,sBACA,2BACA,4BACA,oBAGJ,mCACI,WACA,gBACA,8DAGJ,mCACI,YACA,iBACA,+DAGJ,oCACI,kBACA,WAGJ,uCACI,SAEA,0CACI,kBACA,YAIR,0CACI,mBAEA,4CACI,WC5FZ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAUI,wCACI,YACA,mBAIA,0CACI,oBACA,cACA,WACA,YACA,WACA,sBACA,yBACA,2BACA,4BACA,gBACA,iBAEA,gDACI,yBAIR,sDACI,kEAGJ,sDACI,kEAGJ,sDACI,kEAGJ,wDACI,6DAGJ,0DACI,+DAGJ,+DACI,qEAGJ,+DACI,qEAGJ,wDACI,wDAGJ,yDACI,2DAGJ,wDACI,+DCvEZ;AAAA;AAAA;AAAA;AAAA;AAAA,GAOA,SACI,aACA,kBACA,kBACA,wBACA,aACA,gBACA,WACA,UACA,eAEA,0BACI,YAGJ,yBACI,mBACA,YACA,kBACA,0BAGJ,wBACI,gBAGJ,+BACI,YACA,2BACA,YACA,SACA,QACA,kBACA,oBAGJ,gBACI,yBACA,WACA,iBACA,SACA,iBAGJ,eACI,yBACA,iBACA,SACA,iBCvDR,SACI,aACA,eACA,kBACA,wBACA,aACA,gBACA,WACA,UAEA,kBACI,aAGJ,YACI,mBACA,WACA,gBACA,0BACA,SAGJ,cACI,SACA,yBACA,aCzBR;AAAA;AAAA;AAAA;AAAA;AAAA,GAOA,aACI,aACA,kBACA,kBACA,aACA,0BACA,WACA,YACA,eACA,gBAEA,kBACI,gBACA,qBAIA,2EACI,WACA,2BACA,YACA,SACA,QACA,kBACA,oBAGJ,sCACI,kCACA,iBACA,QACA,gBAKJ,uEACI,SACA,2BACA,YACA,SACA,QACA,kBACA,oBAGJ,oCACI,gCACA,iBACA,UACA,iBCrDJ,kBACI,gBACA,cAGJ,kBACI,SAEA,wBACI,cACA,WACA,cACA,WAGJ,qBACI,WACA,WAGJ,qBACI,YAIR,uBACI,kBAKZ,WACI,WACA,gBAEA,cACI,SACA,UACA,gBAEA,iBACI,SACA,iBACA,eACA,qBACA,kBAIR,yBACI,aCrDR,yLAII,uBACA,gCACA,4BAEA,2PACI,2BACA,UACA,YACA,WACA,oBACA,qBACA,SAIR,aACI,uBACA,iCACA,4BAGJ,YACI,uDAGJ,aACI,0DAGJ,eACI,0DAGJ,mBACI,gEAGJ,oBACI,mEAGJ,oBACI,6DAGJ,sBACI,8DAGJ,gBACI,2DAGJ,aACI,8DAGJ,aACI,+DAGJ,iBACI,8DAGJ,kBACI,oEAIA,sFAEI,SACA,oBACA,+BACA,eAKJ,gBACI,aCnFJ,gBACI,SACA,cACA,mBACA,WAEA,kCACI,iBACA,WACA,mBACA,iBACA,YAIR,eACI,eACA,sBAGJ,gBACI,aAKJ,yCACI,mBAEA,2DACI,mBACA,YAMR,uBACI,WACA,cACA,mBACA,mBAGJ,sBACI,iBAKJ,0CACI,gBAKJ,cACI,kBACA,cAGJ,UACI,mBACA,mBACA,cACA,yBACA,cACA,YAEA,eACI,yBACA,SACA,8BACA,UACA,gBACA,gBAIR,YACI,aAGJ,cACI,aACA,UAKJ,oBACI,mBAIR,iFAKI,YACA,eAEA,+GACI,qBAGJ,+GACI,cACA,qBACA,sBACA,YAIR,wEAII,YAIA,gIAII,YAMA,wBACI,YAKJ,4BACI,WACA,YACA,gBAEA,mCACI,SACA,mBAOZ,cACI,SACA,YAEA,uBACI,UAGJ,2BACI,WACA,gBAIR,iDACI,kBACA,cACA,yBACA,0BAGJ,+BACI,aAIR,mBACI,aAGJ,iBACI,YACA,kBC1LJ,cACI,YACA,yBACA,WACA,aACA,iBAEA,iBACI,UAGJ,sBACI,mBAGJ,8CACI,cACA,cAIR,UACI,kBAEA,4BACI,aACA,eACA,cAGJ,yBACI,kBAEA,2BACI,iBACA,WACA,qBAEA,iCACI,0BCtCZ,0BACI,YAIA,+BACI,kBAEA,0CACI,gBAKJ,0CACI,SAEA,6CACI,gBACA,iBACA,SACA,iBACA,qBAGJ,6CACI,WACA,eACA,SACA,qBAQR,oDACI,WAGJ,kDACI,WAGJ,mDACI,WAGJ,wGACI,WCnDhB,cACI,SACA,UACA,gBAGI,oBACI,SACA,gBAEA,uBACI,eAKJ,wBACI,gBACA,UACA,SAEA,2BACI,eACA,iBAOpB,cACI,gBAGJ,kBACI,gBClCA,qBACI,SACA,UACA,gBACA,gBAEA,kCACI,gBAEA,8CACI,eAGJ,oCACI,kBAIR,wBACI,iBAEA,0BACI,qBCvBhB,gCAEI,UACA,SAEA,8CACI,WACA,aACA,cAGJ,4CACI,SAEA,kDACI,mBAEA,8DACI,iBACA,iBACA,YAGJ,sEACI,YAIA,wFACI,YAKR,4FACI,iBAIR,0FACI,iBAKZ,gBACI,gBAGJ,wBACI,aACA,aAEA,iCACI,gBACA,gBACA,iBCxDR,aACI,gBACA,6BACA,YAEA,6BACI,YAIA,mCACI,YACA,kBACA,WACA,6BAEA,yCACI,WAGJ,uIACI,YAGJ,iDACI,iBAGJ,sCACI,qBACA,SACA,UACA,qBAEA,yCACI,eAOZ,iFACI,eACA,cACA,iBACA,WACA,6BACA,mBAKJ,oDACI,SAKJ,uBACI,gBACA,UAGJ,0BACI,mBAKZ,gBACI,SACA,eAGJ,gBACI,6BACA,WACA,SACA,kBACA,eACA,mBAGJ,gBACI,gCACA,kBAEA,mBACI,eACA,aACA,iBAIA,2BACI,mBACA,WACA,iBACA,SACA,gBACA,mBACA,iBAEA,6BACI,WAIR,iCACI,wBAGJ,iCACI,wBAEA,8CACI,gBAKJ,uCACI,aAIR,mCACI,oBAKZ,wEAEI,4CACA,mBACA,yBACA,cAGJ,qBACI,4CAGJ,iBACI,gBACA,iBAGJ,aACI,YACA,iBACA,gBACA,YACA,WAEA,gBACI,eAKJ,qCACI,WAIR,cACI,aAGJ,gBACI,YACA,kBACA,QACA,UAEA,yCACI,kBACA,gBAEA,qDACI,2BACA,YACA,SACA,QACA,kBACA,oBACA,iCACA,iBACA,SACA,iBAIR,oBACI,SAEA,0BACI,YACA,yBAKJ,2BACI,SACA,sBC/MR,qBACI,gBAIA,wBACI,mBAEA,8BACI,WAKZ,oCACI,YACA,YAGJ,2BACI,iBACA,iBAEA,oCACI,eCxBR,0BACI,mBAGJ,oBACI,YACA,mBAEA,uBACI,WACA,gBAGJ,kCACI,YACA,gBACA,SACA,UAEA,qCACI,gBACA,eAGJ,oCACI,iBAMR,4BACI,qBACA,eACA","file":"default.css"} \ No newline at end of file diff --git a/vendor/traq/views/default/layouts/_head.phtml b/vendor/traq/views/default/layouts/_head.phtml index e83c8bf62..38c08c585 100644 --- a/vendor/traq/views/default/layouts/_head.phtml +++ b/vendor/traq/views/default/layouts/_head.phtml @@ -10,6 +10,8 @@ + + + + + From ec97e1f9ec6caea209612e5dc107b6c575e6db48 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Thu, 30 Jun 2022 23:27:34 +1000 Subject: [PATCH 07/46] FontAwesome --- package.json | 4 ++++ yarn.lock | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/package.json b/package.json index b2fdc80bd..b3dcdadac 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,10 @@ "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" }, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.1.1", + "@fortawesome/free-regular-svg-icons": "^6.1.1", + "@fortawesome/free-solid-svg-icons": "^6.1.1", + "@fortawesome/vue-fontawesome": "^3.0.1", "axios": "^0.27.2", "pinia": "^2.0.14", "vue": "^3.2.37" diff --git a/yarn.lock b/yarn.lock index e31e7c130..a45950113 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27,6 +27,37 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@fortawesome/fontawesome-common-types@6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz#7dc996042d21fc1ae850e3173b5c67b0549f9105" + integrity sha512-wVn5WJPirFTnzN6tR95abCx+ocH+3IFLXAgyavnf9hUmN0CfWoDjPT/BAWsUVwSlYYVBeCLJxaqi7ZGe4uSjBA== + +"@fortawesome/fontawesome-svg-core@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.1.1.tgz#3424ec6182515951816be9b11665d67efdce5b5f" + integrity sha512-NCg0w2YIp81f4V6cMGD9iomfsIj7GWrqmsa0ZsPh59G7PKiGN1KymZNxmF00ssuAlo/VZmpK6xazsGOwzKYUMg== + dependencies: + "@fortawesome/fontawesome-common-types" "6.1.1" + +"@fortawesome/free-regular-svg-icons@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.1.1.tgz#3f2f58262a839edf0643cbacee7a8a8230061c98" + integrity sha512-xXiW7hcpgwmWtndKPOzG+43fPH7ZjxOaoeyooptSztGmJxCAflHZxXNK0GcT0uEsR4jTGQAfGklDZE5NHoBhKg== + dependencies: + "@fortawesome/fontawesome-common-types" "6.1.1" + +"@fortawesome/free-solid-svg-icons@^6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.1.tgz#3369e673f8fe8be2fba30b1ec274d47490a830a6" + integrity sha512-0/5exxavOhI/D4Ovm2r3vxNojGZioPwmFrKg0ZUH69Q68uFhFPs6+dhAToh6VEQBntxPRYPuT5Cg1tpNa9JUPg== + dependencies: + "@fortawesome/fontawesome-common-types" "6.1.1" + +"@fortawesome/vue-fontawesome@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.1.tgz#ced35cefc52b364f7db973f2fe9f50c3dd160715" + integrity sha512-CdXZJoCS+aEPec26ZP7hWWU3SaJlQPZSCGdgpQ2qGl2HUmtUUNrI3zC4XWdn1JUmh3t5OuDeRG1qB4eGRNSD4A== + "@humanwhocodes/config-array@^0.9.2": version "0.9.5" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" From f29abe6447ea43f0bbda0ed8bf41126a4167ace0 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Thu, 30 Jun 2022 23:29:04 +1000 Subject: [PATCH 08/46] Add/remove ticket filters and values --- traq-ui/css/buttons.css | 29 +++- traq-ui/ticket-listing/TicketFilters.vue | 193 +++++++++++++++++++++-- traq-ui/ticket-listing/TicketListing.vue | 7 +- traq-ui/ticket-listing/ticket-listing.ts | 3 +- 4 files changed, 206 insertions(+), 26 deletions(-) diff --git a/traq-ui/css/buttons.css b/traq-ui/css/buttons.css index 6cf1a5ae9..4ced31107 100644 --- a/traq-ui/css/buttons.css +++ b/traq-ui/css/buttons.css @@ -5,10 +5,10 @@ input[type="reset"] { @apply py-2 px-3; @apply border-0 rounded; @apply bg-gray-200 hover:bg-gray-300 active:bg-gray-400; - @apply focus:ring-2 ring-gray-500; + @apply focus:ring-4 ring-gray-400; @apply transition-all; - &.primary { + &.btn-primary { @apply text-white; @apply bg-brand-400; @apply ring-brand-200; @@ -22,16 +22,31 @@ input[type="reset"] { } } - &.success { - @apply bg-emerald-400; - @apply ring-emerald-200; + &.btn-success { + @apply bg-emerald-500; + @apply ring-emerald-400; + @apply text-white; &:hover { - @apply bg-emerald-500; + @apply bg-emerald-600; } &:active { - @apply bg-emerald-600; + @apply bg-emerald-700; + } + } + + &.btn-danger { + @apply bg-red-500; + @apply ring-red-200; + @apply text-white; + + &:hover { + @apply bg-red-600; + } + + &:active { + @apply bg-red-700; } } } diff --git a/traq-ui/ticket-listing/TicketFilters.vue b/traq-ui/ticket-listing/TicketFilters.vue index a6e2236e8..fb1df93c2 100644 --- a/traq-ui/ticket-listing/TicketFilters.vue +++ b/traq-ui/ticket-listing/TicketFilters.vue @@ -1,8 +1,23 @@ @@ -95,22 +145,62 @@ export default {
No filters set.
{{ filter.label }}
-
- - +
+
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+
+ + +
+
+ or +
+
+ +
+
+ +
+
+ +
+
-
- +
- - +
@@ -169,13 +169,13 @@ export default {
- + + - + +
From 45561fe8602c9f9feeb6a4f4d4a151f21f118055 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Thu, 30 Jun 2022 23:45:46 +1000 Subject: [PATCH 10/46] Update success button ring colour --- traq-ui/css/buttons.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/traq-ui/css/buttons.css b/traq-ui/css/buttons.css index 4ced31107..dd6826139 100644 --- a/traq-ui/css/buttons.css +++ b/traq-ui/css/buttons.css @@ -24,7 +24,7 @@ input[type="reset"] { &.btn-success { @apply bg-emerald-500; - @apply ring-emerald-400; + @apply ring-emerald-200; @apply text-white; &:hover { From f7c3538f18d56ee9bad9882eb0107596c3e90156 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Fri, 1 Jul 2022 00:38:03 +1000 Subject: [PATCH 11/46] Tweak filter UI --- traq-ui/css/buttons.css | 69 ++++++++++++++++++++++-- traq-ui/ticket-listing/TicketFilters.vue | 38 +++++++++---- 2 files changed, 94 insertions(+), 13 deletions(-) diff --git a/traq-ui/css/buttons.css b/traq-ui/css/buttons.css index dd6826139..1f0b20bb6 100644 --- a/traq-ui/css/buttons.css +++ b/traq-ui/css/buttons.css @@ -1,3 +1,31 @@ +.btn-group { + & > button { + @apply rounded-none; + + &:first-child { + @apply rounded-l; + } + + &:last-child { + @apply rounded-r; + } + } +} + +.input-group { + & > * { + @apply !rounded-none; + + &:first-child { + @apply !rounded-l; + } + + &:last-child { + @apply !rounded-r; + } + } +} + button, input[type="submit"], input[type="button"], @@ -7,18 +35,37 @@ input[type="reset"] { @apply bg-gray-200 hover:bg-gray-300 active:bg-gray-400; @apply focus:ring-4 ring-gray-400; @apply transition-all; + @apply cursor-pointer; + + &:disabled { + @apply cursor-not-allowed; + + &, + &:hover, + &:active { + @apply bg-gray-100; + } + } &.btn-primary { @apply text-white; - @apply bg-brand-400; + @apply bg-brand-500; @apply ring-brand-200; &:hover { - @apply bg-brand-500; + @apply bg-brand-600; } &:active { - @apply bg-brand-600; + @apply bg-brand-700; + } + + &:disabled { + &, + &:hover, + &:active { + @apply bg-brand-300; + } } } @@ -34,6 +81,14 @@ input[type="reset"] { &:active { @apply bg-emerald-700; } + + &:disabled { + &, + &:hover, + &:active { + @apply bg-emerald-300; + } + } } &.btn-danger { @@ -48,5 +103,13 @@ input[type="reset"] { &:active { @apply bg-red-700; } + + &:disabled { + &, + &:hover, + &:active { + @apply bg-red-300; + } + } } } diff --git a/traq-ui/ticket-listing/TicketFilters.vue b/traq-ui/ticket-listing/TicketFilters.vue index b5b8d4703..457b50647 100644 --- a/traq-ui/ticket-listing/TicketFilters.vue +++ b/traq-ui/ticket-listing/TicketFilters.vue @@ -200,15 +200,20 @@ export default {
- +
+ +
- +
+ + +
@@ -216,15 +221,24 @@ export default { From 06347987a1f69c0330698623cd9b09d715088b3b Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Fri, 1 Jul 2022 00:38:18 +1000 Subject: [PATCH 12/46] Handle ticket filter application --- traq-ui/ticket-listing/TicketListing.vue | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/traq-ui/ticket-listing/TicketListing.vue b/traq-ui/ticket-listing/TicketListing.vue index bc55de064..2b18ad8da 100644 --- a/traq-ui/ticket-listing/TicketListing.vue +++ b/traq-ui/ticket-listing/TicketListing.vue @@ -14,13 +14,28 @@ export default { filters: [], } }, - methods: { - getTickets() { + computed: { + getTicketUrl(): string { let ticketsUrl = window.traq.base + window.traq.project_slug + "/tickets.json" + if (this.sort_by) { ticketsUrl = `${ticketsUrl}?order_by=${this.sort_by}.${this.sort_order}` } - axios.get(ticketsUrl).then((resp) => { + + if (this.filters.length) { + const filterBits = this.filters.map((filter) => { + return filter.field + (filter.condition ? "" : "!") + filter.values.join(",") + }) + + ticketsUrl = ticketsUrl + (this.sort_by ? "&" : "?") + filterBits.join("&") + } + + return ticketsUrl + }, + }, + methods: { + getTickets() { + axios.get(this.getTicketUrl).then((resp) => { this.tickets = resp.data.tickets this.page = resp.data.page this.total_pages = resp.data.total_pages @@ -38,8 +53,8 @@ export default { return `${window.traq.base}users/${userId}` }, applyFilters(filters) { - console.log(filters) this.filters = filters + this.getTickets() }, }, mounted() { From 5031cb63206ae2a9643878dbd2fd7b4ca69f1536 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Fri, 1 Jul 2022 01:36:42 +1000 Subject: [PATCH 13/46] Convert query string to filters --- traq-ui/ticket-listing/TicketFilters.vue | 57 +++++++++++++++++------- traq-ui/ticket-listing/TicketListing.vue | 8 +++- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/traq-ui/ticket-listing/TicketFilters.vue b/traq-ui/ticket-listing/TicketFilters.vue index 457b50647..66c79527e 100644 --- a/traq-ui/ticket-listing/TicketFilters.vue +++ b/traq-ui/ticket-listing/TicketFilters.vue @@ -7,32 +7,31 @@ interface FilterOption { field: string label: string type: "is" | "isOr" | "contains" - options?: Array<{ label: string; value: string }> - condition?: boolean + dataSet?: string value?: Array count?: number } -interface Filter { - field: string - condition: boolean - values: Array -} - export default { data() { return { isExpanded: false, filters: [], - milestones: [], + filterData: { + milestones: [], + components: [], + statuses: [], + }, } }, mounted() { + this.convertQueryString() + const roadmapUrl = window.traq.base + window.traq.project_slug + "/roadmap.json" axios.get(roadmapUrl).then((resp) => { - this.milestones = + this.filterData.milestones = resp.data.map((data) => ({ label: data.name, value: data.slug, @@ -72,13 +71,13 @@ export default { field: "milestone", label: "Milestone", type: "is", - options: this.milestones, + dataSet: "milestones", }, { field: "version", label: "Version", type: "is", - options: this.milestones, + dataSet: "milestones", }, { field: "status", @@ -104,7 +103,11 @@ export default { toggleExpand() { this.isExpanded = !this.isExpanded }, - applyFilters() { + applyFilters(filters = null) { + if (Array.isArray(filters)) { + this.filters = filters + } + this.$emit("applyFilters", this.filters) }, addFilter(event) { @@ -134,6 +137,28 @@ export default { filter.values.splice(valueIndex, 1) } }, + convertQueryString() { + const filters = [] + const params = new URLSearchParams(window.location.search.substring(1)) + + // Loop over filter options and check params for a value + this.filterOptions.map((filter: FilterOption) => { + const paramValue = params.get(filter.field) + + if (paramValue) { + const condition = !paramValue.startsWith("!") + const values = !condition ? paramValue.substring(1)?.split(",") : paramValue.split(",") + + filters.push({ + ...filter, + condition, + values: values ?? [], + }) + } + }) + + this.applyFilters(filters) + }, }, } @@ -155,7 +180,7 @@ export default {
@@ -201,7 +226,7 @@ export default {
- +
@@ -212,7 +237,7 @@ export default { {{ option.label }} - +
diff --git a/traq-ui/ticket-listing/TicketListing.vue b/traq-ui/ticket-listing/TicketListing.vue index 2b18ad8da..39ef99019 100644 --- a/traq-ui/ticket-listing/TicketListing.vue +++ b/traq-ui/ticket-listing/TicketListing.vue @@ -24,7 +24,7 @@ export default { if (this.filters.length) { const filterBits = this.filters.map((filter) => { - return filter.field + (filter.condition ? "" : "!") + filter.values.join(",") + return `${filter.field}=` + (filter.condition ? "" : "!") + filter.values.join(",") }) ticketsUrl = ticketsUrl + (this.sort_by ? "&" : "?") + filterBits.join("&") @@ -40,6 +40,8 @@ export default { this.page = resp.data.page this.total_pages = resp.data.total_pages }) + + this.updateUrl() }, sortTickets(column) { this.sort_order = column !== this.sort_by || this.sort_order === "desc" ? "asc" : "desc" @@ -56,6 +58,10 @@ export default { this.filters = filters this.getTickets() }, + updateUrl() { + // Update page URL to the same URL to fetch tickets without the .json extension. + history.pushState({}, null, this.getTicketUrl.replace(".json", "")) + }, }, mounted() { this.getTickets() From 09a640ad636090583cee7cef6b64ea0719ebef06 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Fri, 1 Jul 2022 23:52:52 +1000 Subject: [PATCH 14/46] Add Luxon --- package.json | 1 + yarn.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/package.json b/package.json index b3dcdadac..3215ba016 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@fortawesome/free-solid-svg-icons": "^6.1.1", "@fortawesome/vue-fontawesome": "^3.0.1", "axios": "^0.27.2", + "luxon": "^2.4.0", "pinia": "^2.0.14", "vue": "^3.2.37" }, diff --git a/yarn.lock b/yarn.lock index a45950113..e3d45ae11 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1717,6 +1717,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +luxon@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.4.0.tgz#9435806545bb32d4234dab766ab8a3d54847a765" + integrity sha512-w+NAwWOUL5hO0SgwOHsMBAmZ15SoknmQXhSO0hIbJCAmPKSsGeK8MlmhYh2w6Iib38IxN2M+/ooXWLbeis7GuA== + magic-string@^0.25.7: version "0.25.9" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" From 1a037d20245b79e348782ab4bb2be196db97a92d Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Fri, 1 Jul 2022 23:53:38 +1000 Subject: [PATCH 15/46] Ticket filter styling and animation --- traq-ui/css/forms.css | 30 ++++++++++++++++++++ traq-ui/ticket-listing/TicketFilters.vue | 35 +++++++++--------------- 2 files changed, 43 insertions(+), 22 deletions(-) create mode 100644 traq-ui/css/forms.css diff --git a/traq-ui/css/forms.css b/traq-ui/css/forms.css new file mode 100644 index 000000000..c8d1db1f7 --- /dev/null +++ b/traq-ui/css/forms.css @@ -0,0 +1,30 @@ +select, +input[type="text"], +input[type="password"] { + @apply border border-gray-400 focus:border-brand-600 rounded; + @apply focus:ring-4 focus:ring-brand-100; + @apply text-sm text-gray-800; + @apply py-2 px-3; + @apply transition-all; + @apply text-sm; +} + +select, +input[type="text"], +input[type="password"], +input[type="button"], +input[type="reset"], +button { + line-height: 1.25; + @apply box-border; + @apply appearance-none; + /* @apply min-h-[32px]; */ +} + +select:not([multiple]) { + padding-right: 30px; + background-repeat: no-repeat; + background-position: right 0.75rem center; + background-size: 10px; + background-image: url('data:image/svg+xml,'); +} diff --git a/traq-ui/ticket-listing/TicketFilters.vue b/traq-ui/ticket-listing/TicketFilters.vue index 66c79527e..d3a9d4e98 100644 --- a/traq-ui/ticket-listing/TicketFilters.vue +++ b/traq-ui/ticket-listing/TicketFilters.vue @@ -16,7 +16,7 @@ interface FilterOption { export default { data() { return { - isExpanded: false, + isExpanded: true, filters: [], filterData: { milestones: [], @@ -164,9 +164,9 @@ export default { diff --git a/traq-ui/ticket-listing/TicketListing.vue b/traq-ui/ticket-listing/TicketListing.vue index 39ef99019..0b147305f 100644 --- a/traq-ui/ticket-listing/TicketListing.vue +++ b/traq-ui/ticket-listing/TicketListing.vue @@ -1,9 +1,12 @@ + + + + + + + + + + + + @@ -90,7 +98,7 @@ export default {

Tickets

- + @@ -185,6 +193,15 @@ export default { + @@ -209,6 +226,9 @@ export default { +
{{ ticket.id }}{{ formatDate(ticket.created_at) }} {{ formatDate(ticket.updated_at) }} {{ ticket.votes }}
From 7ec42eafc18e8b6c0bb62cf2b36fb8411f656bd2 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Sat, 2 Jul 2022 18:54:12 +1000 Subject: [PATCH 27/46] Custom field filtering --- traq-ui/ticket-listing/TicketFilters.vue | 145 ++++++++++++-------- traq-ui/ticket-listing/TicketListing.vue | 2 +- vendor/traq/helpers/ticket_filter_query.php | 10 +- vendor/traq/models/custom_field.php | 2 +- 4 files changed, 96 insertions(+), 63 deletions(-) diff --git a/traq-ui/ticket-listing/TicketFilters.vue b/traq-ui/ticket-listing/TicketFilters.vue index 61f38c2f0..963943146 100644 --- a/traq-ui/ticket-listing/TicketFilters.vue +++ b/traq-ui/ticket-listing/TicketFilters.vue @@ -14,6 +14,8 @@ interface FilterOption { } export default { + props: ["customFields"], + data() { return { isExpanded: true, @@ -24,6 +26,58 @@ export default { statuses: [], priorities: [], }, + availableFilters: [ + { + field: "summary", + label: "Summary", + type: "contains", + }, + { + field: "description", + label: "Description", + type: "contains", + }, + { + field: "owner", + label: "Owner", + type: "isOr", + }, + { + field: "assigned_to", + label: "Assigned to", + type: "isOr", + }, + { + field: "component", + label: "Component", + type: "is", + dataSet: "components", + }, + { + field: "milestone", + label: "Milestone", + type: "is", + dataSet: "milestones", + }, + { + field: "version", + label: "Version", + type: "is", + dataSet: "milestones", + }, + { + field: "status", + label: "Status", + type: "is", + dataSet: "statuses", + }, + { + field: "priority", + label: "Priorities", + type: "is", + dataSet: "priorities", + }, + ], } }, @@ -84,60 +138,33 @@ export default { }) }, + watch: { + customFields(): void { + this.customFields.map((field) => { + const key = field.slug.replaceAll("-", "_") + + if (Array.isArray(field.values)) { + this.filterData[key] = field.values.map((value) => ({ label: value, value })) + } + + // Multi-select or contains will do just fine + const fieldType: "is" | "contains" = field.type === "select" ? "is" : "contains" + + const filter: FilterOption = { + label: field.name, + field: field.slug, + type: fieldType, + dataSet: fieldType === "is" ? key : undefined, + } + + this.availableFilters.push(filter) + }) + }, + }, + computed: { filterOptions(): FilterOption[] { - const options: FilterOption[] = [ - { - field: "summary", - label: "Summary", - type: "contains", - }, - { - field: "description", - label: "Description", - type: "contains", - }, - { - field: "owner", - label: "Owner", - type: "isOr", - }, - { - field: "assigned_to", - label: "Assigned to", - type: "isOr", - }, - { - field: "component", - label: "Component", - type: "is", - dataSet: "components", - }, - { - field: "milestone", - label: "Milestone", - type: "is", - dataSet: "milestones", - }, - { - field: "version", - label: "Version", - type: "is", - dataSet: "milestones", - }, - { - field: "status", - label: "Status", - type: "is", - dataSet: "statuses", - }, - { - field: "priority", - label: "Priorities", - type: "is", - dataSet: "priorities", - }, - ] + const options: FilterOption[] = this.availableFilters const filters = options.filter((filter: FilterOption) => { const added = this.filters.find((option: FilterOption) => option.field === filter.field) @@ -153,17 +180,17 @@ export default { }, methods: { - toggleExpand() { + toggleExpand(): void { this.isExpanded = !this.isExpanded }, - applyFilters(filters = null) { + applyFilters(filters = null): void { if (Array.isArray(filters)) { this.filters = filters } this.$emit("applyFilters", this.filters) }, - addFilter(event) { + addFilter(event): void { const field: string = event.target.value const filter: FilterOption = this.filterOptions.find((option: FilterOption) => option.field === field) @@ -177,20 +204,20 @@ export default { event.target.value = "" }, - addFilterValue(filter) { + addFilterValue(filter): void { filter.values.push("") }, - setFilterValue(filter, index, event) { + setFilterValue(filter, index, event): void { filter.values[index] = event.target.value }, - removeFilter(filter, valueIndex = null) { + removeFilter(filter, valueIndex = null): void { if (valueIndex === null || (valueIndex === 0 && filter.values.length === 1)) { this.filters = this.filters.filter((option: FilterOption) => option.field !== filter.field) } else { filter.values.splice(valueIndex, 1) } }, - convertQueryString() { + convertQueryString(): void { const activeFilters = [] const params = new URLSearchParams(window.location.search.substring(1)) diff --git a/traq-ui/ticket-listing/TicketListing.vue b/traq-ui/ticket-listing/TicketListing.vue index d644ba0b0..b54bdaa32 100644 --- a/traq-ui/ticket-listing/TicketListing.vue +++ b/traq-ui/ticket-listing/TicketListing.vue @@ -97,7 +97,7 @@ export default {

Tickets

- + diff --git a/vendor/traq/helpers/ticket_filter_query.php b/vendor/traq/helpers/ticket_filter_query.php index b7f11d3f1..8053c683d 100644 --- a/vendor/traq/helpers/ticket_filter_query.php +++ b/vendor/traq/helpers/ticket_filter_query.php @@ -202,8 +202,12 @@ private function add($field, $condition, $values) // Sort values low to high asort($values); + $values = array_map( + fn($val) => '"'.$val.'"', + $values + ); - if (count($values) == 1 && !empty($values[0])) { + if (count($values) >= 1 && !empty($values[0])) { $this->custom_field_sql[] = " `fields`.`custom_field_id` = {$custom_field->id} AND `fields`.`value` IN ('" . implode("','", $values) . "') @@ -233,7 +237,9 @@ public function sql() $sql = array(); if (count($this->custom_field_sql)) { - $sql[] = "JOIN `" . Database::connection()->prefix . "custom_field_values` AS `fields` ON (" . implode(' AND ', $this->custom_field_sql) . ")"; + foreach ($this->custom_field_sql as $index => $customFieldSql) { + $sql[] = "JOIN `" . Database::connection()->prefix . "custom_field_values` AS `fields${index}` ON (" . str_replace('`fields`', "`fields${index}`", $customFieldSql) . ")"; + } } $sql[] = " WHERE `project_id` = {$this->project->id}"; diff --git a/vendor/traq/models/custom_field.php b/vendor/traq/models/custom_field.php index 0e120fc8d..119555b3e 100644 --- a/vendor/traq/models/custom_field.php +++ b/vendor/traq/models/custom_field.php @@ -397,7 +397,7 @@ public function __toArray($fields = null) 'max_length' => (int) $this->max_length, 'multiple' => (bool) $this->multiple, 'regex' => $this->regex, - 'values' => $this->values, + 'values' => $this->type === 'select' ? explode("\n", $this->values) : $this->values, 'default_value' => $this->default_value, 'ticket_type_ids' => $ticketTypeIds, ]; From c5a22885f88f6dc5bb12420a9b3387cd41bed98a Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Sat, 2 Jul 2022 19:29:15 +1000 Subject: [PATCH 28/46] Update custom field type options --- traq-ui/ticket-listing/TicketColumns.vue | 1 + traq-ui/ticket-listing/TicketFilters.vue | 6 ++++-- traq-ui/ticket-listing/TicketListing.vue | 4 ++++ vendor/traq/helpers/ticket_filter_query.php | 4 +++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/traq-ui/ticket-listing/TicketColumns.vue b/traq-ui/ticket-listing/TicketColumns.vue index d60ed410f..ba923109a 100644 --- a/traq-ui/ticket-listing/TicketColumns.vue +++ b/traq-ui/ticket-listing/TicketColumns.vue @@ -77,6 +77,7 @@ export default { watch: { customFields() { + // Map custom fields to available columns. this.customFields.map((field) => { this.columns.push({ name: field.name, diff --git a/traq-ui/ticket-listing/TicketFilters.vue b/traq-ui/ticket-listing/TicketFilters.vue index 963943146..1e5b27b65 100644 --- a/traq-ui/ticket-listing/TicketFilters.vue +++ b/traq-ui/ticket-listing/TicketFilters.vue @@ -140,15 +140,17 @@ export default { watch: { customFields(): void { + // Map custom fields to available filters. this.customFields.map((field) => { const key = field.slug.replaceAll("-", "_") + // If the values are an array, create a data set. if (Array.isArray(field.values)) { this.filterData[key] = field.values.map((value) => ({ label: value, value })) } - // Multi-select or contains will do just fine - const fieldType: "is" | "contains" = field.type === "select" ? "is" : "contains" + // is/isOr will do just fine + const fieldType: "is" | "isOr" = field.type === "select" ? "is" : "isOr" const filter: FilterOption = { label: field.name, diff --git a/traq-ui/ticket-listing/TicketListing.vue b/traq-ui/ticket-listing/TicketListing.vue index b54bdaa32..abc2a7849 100644 --- a/traq-ui/ticket-listing/TicketListing.vue +++ b/traq-ui/ticket-listing/TicketListing.vue @@ -7,6 +7,7 @@ import TicketColumns from "./TicketColumns.vue" export default { components: { TicketFilters, TicketColumns }, + data() { return { tickets: [], @@ -33,10 +34,12 @@ export default { getTicketUrl(): string { let ticketsUrl = window.traq.base + window.traq.project_slug + "/tickets.json" + // Add sorting options to URL if (this.sort_by) { ticketsUrl = `${ticketsUrl}?order_by=${this.sort_by}.${this.sort_order}` } + // Add filters to URL if (this.filters.length) { const filterBits = this.filters.map((filter) => { return `${filter.field}=` + (filter.condition ? "" : "!") + filter.values.join(",") @@ -47,6 +50,7 @@ export default { return ticketsUrl }, + // Icons faChevronUp() { return faChevronUp }, diff --git a/vendor/traq/helpers/ticket_filter_query.php b/vendor/traq/helpers/ticket_filter_query.php index 8053c683d..0932a9b38 100644 --- a/vendor/traq/helpers/ticket_filter_query.php +++ b/vendor/traq/helpers/ticket_filter_query.php @@ -60,6 +60,8 @@ public function process($field, $values) $values = explode(',', $values); } + $values = array_map(fn($val) => addslashes($val), $values); + $condition = ''; if (substr($values[0], 0, 1) == '!') { $condition = 'NOT'; @@ -210,7 +212,7 @@ private function add($field, $condition, $values) if (count($values) >= 1 && !empty($values[0])) { $this->custom_field_sql[] = " `fields`.`custom_field_id` = {$custom_field->id} - AND `fields`.`value` IN ('" . implode("','", $values) . "') + AND `fields`.`value` ${condition} IN ('" . implode("','", $values) . "') AND `fields`.`ticket_id` = `" . Database::connection()->prefix . "tickets`.`id` "; } From 6508bd39c03e83a4a55b06c1a2e3d3164851e29f Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Sat, 2 Jul 2022 22:09:50 +1000 Subject: [PATCH 29/46] Promises everywhere --- .vscode/settings.json | 3 +- traq-ui/ticket-listing/TicketColumns.vue | 20 ++-- traq-ui/ticket-listing/TicketFilters.vue | 129 ++++++++++++----------- traq-ui/ticket-listing/TicketListing.vue | 6 +- 4 files changed, 86 insertions(+), 72 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d523d01e4..c3bbe1ee2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,7 @@ "editor.formatOnSave": true, "cSpell.ignoreWords": [ "TRAQ", - "axios" + "axios", + "roadmap" ] } \ No newline at end of file diff --git a/traq-ui/ticket-listing/TicketColumns.vue b/traq-ui/ticket-listing/TicketColumns.vue index ba923109a..a4b9dd405 100644 --- a/traq-ui/ticket-listing/TicketColumns.vue +++ b/traq-ui/ticket-listing/TicketColumns.vue @@ -72,19 +72,13 @@ export default { }, mounted() { + this.buildCustomFields() this.$emit("apply-columns", this.active) }, watch: { customFields() { - // Map custom fields to available columns. - this.customFields.map((field) => { - this.columns.push({ - name: field.name, - field: field.slug, - custom: true, - }) - }) + this.buildCustomFields() }, }, @@ -101,6 +95,16 @@ export default { this.$emit("apply-columns", this.active) }, + buildCustomFields(): void { + // Map custom fields to available columns. + this.customFields.map((field) => { + this.columns.push({ + name: field.name, + field: field.slug, + custom: true, + }) + }) + }, }, } diff --git a/traq-ui/ticket-listing/TicketFilters.vue b/traq-ui/ticket-listing/TicketFilters.vue index 1e5b27b65..457b784d3 100644 --- a/traq-ui/ticket-listing/TicketFilters.vue +++ b/traq-ui/ticket-listing/TicketFilters.vue @@ -87,80 +87,56 @@ export default { const statusesUrl = window.traq.base + "api/statuses" const prioritiesUrl = window.traq.base + "api/priorities" - axios.get(roadmapUrl).then((resp) => { - this.filterData.milestones = - resp.data.map((data) => ({ - label: data.name, - value: data.slug, - })) ?? [] - }) - - axios.get(statusesUrl).then((resp) => { - const open = - resp.data - .filter((status) => status.status === 1) - .map((data) => ({ + Promise.all([axios.get(roadmapUrl), axios.get(statusesUrl), axios.get(prioritiesUrl), axios.get(componentsUrl)]).then( + ([roadmap, statuses, priorities, components]) => { + this.filterData.milestones = + roadmap.data.map((data) => ({ + label: data.name, + value: data.slug, + })) ?? [] + + const open = + statuses.data + .filter((status) => status.status === 1) + .map((data) => ({ + label: data.name, + value: data.name, + })) ?? [] + + const closed = + statuses.data + .filter((status) => status.status === 0) + .map((data) => ({ + label: data.name, + value: data.name, + })) ?? [] + + this.filterData.statuses = { + Open: open, + Closed: closed, + } + + this.filterData.priorities = + priorities.data.map((data) => ({ label: data.name, value: data.name, })) ?? [] - const closed = - resp.data - .filter((status) => status.status === 0) - .map((data) => ({ + this.filterData.components = + components.data.map((data) => ({ label: data.name, value: data.name, })) ?? [] - this.filterData.statuses = { - Open: open, - Closed: closed, + // Convert query string after we get statuses, as we convert 'allOpen' and 'allClosed' + this.buildCustomFields().then(() => this.convertQueryString()) } - - // Convert query string after we get statuses, as we convert 'allOpen' and 'allClosed' - this.convertQueryString() - }) - - axios.get(prioritiesUrl).then((resp) => { - this.filterData.priorities = - resp.data.map((data) => ({ - label: data.name, - value: data.name, - })) ?? [] - }) - - axios.get(componentsUrl).then((resp) => { - this.filterData.components = - resp.data.map((data) => ({ - label: data.name, - value: data.name, - })) ?? [] - }) + ) }, watch: { customFields(): void { - // Map custom fields to available filters. - this.customFields.map((field) => { - const key = field.slug.replaceAll("-", "_") - - // If the values are an array, create a data set. - if (Array.isArray(field.values)) { - this.filterData[key] = field.values.map((value) => ({ label: value, value })) - } - - // is/isOr will do just fine - const fieldType: "is" | "isOr" = field.type === "select" ? "is" : "isOr" - - const filter: FilterOption = { - label: field.name, - field: field.slug, - type: fieldType, - dataSet: fieldType === "is" ? key : undefined, - } - - this.availableFilters.push(filter) - }) + this.buildCustomFields() }, }, @@ -219,6 +195,37 @@ export default { filter.values.splice(valueIndex, 1) } }, + buildCustomFields(): Promise { + return new Promise((resolve, reject) => { + try { + // Map custom fields to available filters. + this.customFields.map((field) => { + const key = field.slug.replaceAll("-", "_") + + // If the values are an array, create a data set. + if (Array.isArray(field.values)) { + this.filterData[key] = field.values.map((value) => ({ label: value, value })) + } + + // is/isOr will do just fine + const fieldType: "is" | "isOr" = field.type === "select" ? "is" : "isOr" + + const filter: FilterOption = { + label: field.name, + field: field.slug, + type: fieldType, + dataSet: fieldType === "is" ? key : undefined, + } + + this.availableFilters.push(filter) + }) + + resolve(true) + } catch (error) { + reject(false) + } + }) + }, convertQueryString(): void { const activeFilters = [] const params = new URLSearchParams(window.location.search.substring(1)) diff --git a/traq-ui/ticket-listing/TicketListing.vue b/traq-ui/ticket-listing/TicketListing.vue index abc2a7849..758206597 100644 --- a/traq-ui/ticket-listing/TicketListing.vue +++ b/traq-ui/ticket-listing/TicketListing.vue @@ -10,6 +10,7 @@ export default { data() { return { + isLoading: true, tickets: [], page: 1, total_pages: 1, @@ -27,6 +28,7 @@ export default { const customFieldsUrl = `${window.traq.base}api/${window.traq.project_slug}/custom-fields` axios.get(customFieldsUrl).then((resp) => { this.customFields = resp.data + this.isLoading = false }) }, @@ -101,8 +103,8 @@ export default {

Tickets

- - + +
From 77d031064f614ec2ed2b67e02d460d29c8afc13d Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Sat, 2 Jul 2022 22:13:01 +1000 Subject: [PATCH 30/46] Update filter name width --- traq-ui/ticket-listing/TicketFilters.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/traq-ui/ticket-listing/TicketFilters.vue b/traq-ui/ticket-listing/TicketFilters.vue index 457b784d3..f0ff38437 100644 --- a/traq-ui/ticket-listing/TicketFilters.vue +++ b/traq-ui/ticket-listing/TicketFilters.vue @@ -388,7 +388,7 @@ export default { & .label { text-align: right; font-weight: bold; - @apply w-28; + @apply w-32; @apply mr-3; @apply pt-2; } From 34c35ba32bb7b3d58a5166bac5e270229045665f Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Sat, 2 Jul 2022 22:49:33 +1000 Subject: [PATCH 31/46] Implement auth info endpoint --- vendor/traq/config/routes.php | 2 + vendor/traq/controllers/api.php | 26 ++++++++++++ vendor/traq/models/user.php | 42 ++++++++++++------- vendor/traq/views/default/layouts/_head.phtml | 2 +- 4 files changed, 57 insertions(+), 15 deletions(-) diff --git a/vendor/traq/config/routes.php b/vendor/traq/config/routes.php index a1a86d543..9e5b3eba6 100644 --- a/vendor/traq/config/routes.php +++ b/vendor/traq/config/routes.php @@ -33,6 +33,8 @@ Router::add('/users/validate/(.*)', 'traq::controllers::Users.validate/$1'); // API +Router::add('/api/auth', 'traq::controllers::API.auth'); +Router::add('/api/auth/'.PROJECT_SLUG, 'traq::controllers::API.auth'); Router::add('/api/statuses', 'traq::controllers::API.statuses'); Router::add('/api/priorities', 'traq::controllers::API.priorities'); Router::add('/api/'.PROJECT_SLUG.'/components', 'traq::controllers::API.components'); diff --git a/vendor/traq/controllers/api.php b/vendor/traq/controllers/api.php index e82e3ba71..6a98ce129 100644 --- a/vendor/traq/controllers/api.php +++ b/vendor/traq/controllers/api.php @@ -27,6 +27,7 @@ use avalon\core\Load; use traq\models\Status; use traq\models\Priority; +use traq\models\Permission; /** * API controller. @@ -84,4 +85,29 @@ public function action_customFields() { Body::append(to_json($this->project->custom_fields->exec()->fetch_all())); } + + public function action_auth() + { + $data = $this->user->__toArray([ + 'id', + 'username', + 'name', + 'group_id', + 'locale', + ]); + + $data['id'] = (int) $data['id']; + $data['group_id'] = (int) $data['group_id']; + + $data['permissions'] = []; + if ($this->project) { + foreach ($this->user->getPermissions($this->project->id) as $permission) { + if ((bool) $permission->value) { + $data['permissions'][$permission->action] = (bool) $permission->value; + } + } + } + + Body::append(to_json($data)); + } } diff --git a/vendor/traq/models/user.php b/vendor/traq/models/user.php index 497bca84c..6f69fdcbb 100644 --- a/vendor/traq/models/user.php +++ b/vendor/traq/models/user.php @@ -145,28 +145,42 @@ public function permission($project_id, $action) return true; } - // Check if the projects permissions has been fetched + $perms = $this->getPermissions($project_id, $action); + + if (!isset($perms[$action])) { + return false; + } + + return $perms[$action]->value; + } + + /** + * Get the users permissions + * + * @param int $projectId + * + * @return array + */ + public function getPermissions(int $projectId): array + { + // Check if the projects permissions has been fetched // if not, fetch them. - if (!isset($this->permissions['project'][$project_id])) { - $this->permissions['project'][$project_id] = Permission::get_permissions($project_id, $this->_data['group_id']); + if (!isset($this->permissions['project'][$projectId])) { + $this->permissions['project'][$projectId] = Permission::get_permissions($projectId, $this->_data['group_id']); } // Check if the user has a role for the project and // fetch the permissions if not already done so... - $role_id = $this->get_project_role($project_id); - if ($role_id and !isset($this->permissions['role'][$project_id])) { - $this->permissions['role'][$project_id] = Permission::get_permissions($project_id, $role_id, 'role'); - } elseif (!isset($this->permissions['role'][$project_id])) { - $this->permissions['role'][$project_id] = array(); + $roleId = $this->get_project_role($projectId); + if ($roleId and !isset($this->permissions['role'][$projectId])) { + $this->permissions['role'][$projectId] = Permission::get_permissions($projectId, $roleId, 'role'); + } elseif (!isset($this->permissions['role'][$projectId])) { + $this->permissions['role'][$projectId] = array(); } - $perms = array_merge($this->permissions['project'][$project_id], $this->permissions['role'][$project_id]); - - if (!isset($perms[$action])) { - return false; - } + $perms = array_merge($this->permissions['project'][$projectId], $this->permissions['role'][$projectId]); - return $perms[$action]->value; + return $perms; } /** diff --git a/vendor/traq/views/default/layouts/_head.phtml b/vendor/traq/views/default/layouts/_head.phtml index 5bf33b872..f2dc4dab8 100644 --- a/vendor/traq/views/default/layouts/_head.phtml +++ b/vendor/traq/views/default/layouts/_head.phtml @@ -18,7 +18,7 @@ - + From 149d06469991528cd9d0bd341b8c0a515f61f0c3 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Sat, 2 Jul 2022 23:07:34 +1000 Subject: [PATCH 32/46] Strip comments from install.sql before executing --- install/index.php | 1 + 1 file changed, 1 insertion(+) diff --git a/install/index.php b/install/index.php index 88db09c56..b6b0c7815 100644 --- a/install/index.php +++ b/install/index.php @@ -142,6 +142,7 @@ // Fetch the install SQL. $install_sql = file_get_contents('./install.sql'); $install_sql = str_replace('traq_', $_SESSION['db']['prefix'], $install_sql); + $install_sql = preg_replace('/^(#.*)$/m', '', $install_sql); $queries = explode(';', $install_sql); // Run the install queries. From 6774b931fc2c44c4e7f0cf40e89e14df7a16fc34 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Sat, 2 Jul 2022 23:36:43 +1000 Subject: [PATCH 33/46] Begin Mass Actions component --- traq-ui/ticket-listing/MassActions.vue | 13 +++++++++ traq-ui/ticket-listing/TicketListing.vue | 34 ++++++++++++++++++++---- 2 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 traq-ui/ticket-listing/MassActions.vue diff --git a/traq-ui/ticket-listing/MassActions.vue b/traq-ui/ticket-listing/MassActions.vue new file mode 100644 index 000000000..ac97acdbc --- /dev/null +++ b/traq-ui/ticket-listing/MassActions.vue @@ -0,0 +1,13 @@ + + + + + diff --git a/traq-ui/ticket-listing/TicketListing.vue b/traq-ui/ticket-listing/TicketListing.vue index 758206597..d3ce39e59 100644 --- a/traq-ui/ticket-listing/TicketListing.vue +++ b/traq-ui/ticket-listing/TicketListing.vue @@ -4,13 +4,15 @@ import axios from "axios" import { DateTime } from "luxon" import TicketFilters from "./TicketFilters.vue" import TicketColumns from "./TicketColumns.vue" +import MassActions from "./MassActions.vue" export default { - components: { TicketFilters, TicketColumns }, + components: { TicketFilters, TicketColumns, MassActions }, data() { return { isLoading: true, + user: null, tickets: [], page: 1, total_pages: 1, @@ -19,15 +21,19 @@ export default { filters: [], columns: ["ticket_id", "summary", "status", "type", "owner", "component", "milestone"], customFields: [], + checkedTickets: [], } }, mounted() { + const customFieldsUrl = `${window.traq.base}api/${window.traq.project_slug}/custom-fields` + const authUrl = `${window.traq.base}api/auth/${window.traq.project_slug}` + this.getTickets() - const customFieldsUrl = `${window.traq.base}api/${window.traq.project_slug}/custom-fields` - axios.get(customFieldsUrl).then((resp) => { - this.customFields = resp.data + Promise.all([axios.get(customFieldsUrl), axios.get(authUrl)]).then(([customFields, auth]) => { + this.customFields = customFields.data + this.user = auth.data this.isLoading = false }) }, @@ -95,6 +101,16 @@ export default { // Update page URL to the same URL to fetch tickets without the .json extension. history.pushState({}, null, this.getTicketUrl.replace(".json", "")) }, + userCan(action): boolean { + return this.user?.permissions[action] ?? false + }, + toggleTicket(ticketId): void { + if (this.checkedTickets.includes(ticketId)) { + this.checkedTickets = this.checkedTickets.filter((checkedId) => checkedId !== ticketId) + } else { + this.checkedTickets.push(ticketId) + } + }, }, } @@ -108,6 +124,9 @@ export default {
+ - + +
+ + ID
{{ ticket.id }} + + {{ ticket.ticket_id }} {{ ticket.summary }} @@ -238,6 +260,8 @@ export default {
+ + + From aa78f492f2109c113d5e6dca49be904ea5b2ddc8 Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Sun, 3 Jul 2022 19:55:03 +1000 Subject: [PATCH 37/46] Add Type filter --- traq-ui/ticket-listing/TicketFilters.vue | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/traq-ui/ticket-listing/TicketFilters.vue b/traq-ui/ticket-listing/TicketFilters.vue index f0ff38437..cc77cdd2d 100644 --- a/traq-ui/ticket-listing/TicketFilters.vue +++ b/traq-ui/ticket-listing/TicketFilters.vue @@ -25,6 +25,7 @@ export default { components: [], statuses: [], priorities: [], + types: [], }, availableFilters: [ { @@ -37,6 +38,12 @@ export default { label: "Description", type: "contains", }, + { + field: "type", + label: "Type", + type: "is", + dataSet: "types", + }, { field: "owner", label: "Owner", @@ -82,13 +89,14 @@ export default { }, mounted() { - const roadmapUrl = window.traq.base + window.traq.project_slug + "/roadmap.json" + const roadmapUrl = window.traq.base + window.traq.project_slug + "/roadmap/all.json" const componentsUrl = window.traq.base + "api/" + window.traq.project_slug + "/components" const statusesUrl = window.traq.base + "api/statuses" const prioritiesUrl = window.traq.base + "api/priorities" + const typesUrl = window.traq.base + "api/types" - Promise.all([axios.get(roadmapUrl), axios.get(statusesUrl), axios.get(prioritiesUrl), axios.get(componentsUrl)]).then( - ([roadmap, statuses, priorities, components]) => { + Promise.all([axios.get(roadmapUrl), axios.get(statusesUrl), axios.get(prioritiesUrl), axios.get(componentsUrl), axios.get(typesUrl)]).then( + ([roadmap, statuses, priorities, components, ticketTypes]) => { this.filterData.milestones = roadmap.data.map((data) => ({ label: data.name, @@ -128,6 +136,12 @@ export default { value: data.name, })) ?? [] + this.filterData.types = + ticketTypes.data.map((data) => ({ + label: data.name, + value: data.name, + })) ?? [] + // Convert query string after we get statuses, as we convert 'allOpen' and 'allClosed' this.buildCustomFields().then(() => this.convertQueryString()) } From c6dc17e449fb2e370e4b51e0cd2b5d27034d916e Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Sun, 3 Jul 2022 23:29:17 +1000 Subject: [PATCH 38/46] Update Avalon --- vendor/avalon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/avalon b/vendor/avalon index 5b53b2498..85e862a1d 160000 --- a/vendor/avalon +++ b/vendor/avalon @@ -1 +1 @@ -Subproject commit 5b53b249886531801197b7e915deaa5fef420f17 +Subproject commit 85e862a1de45cfc90f71ada3c982160b22d4d9a8 From 024f6be7264505bf84203cb0c1eebcf49d6ae92e Mon Sep 17 00:00:00 2001 From: Jack Polgar Date: Sun, 3 Jul 2022 23:30:09 +1000 Subject: [PATCH 39/46] Add project members endpoint --- traq-ui/ticket-listing/MassActions.vue | 119 +++++++++++++---------- traq-ui/ticket-listing/TicketFilters.vue | 99 +++++++++++-------- 2 files changed, 124 insertions(+), 94 deletions(-) diff --git a/traq-ui/ticket-listing/MassActions.vue b/traq-ui/ticket-listing/MassActions.vue index b47be4ddb..dfa9f2f17 100644 --- a/traq-ui/ticket-listing/MassActions.vue +++ b/traq-ui/ticket-listing/MassActions.vue @@ -21,64 +21,81 @@ export default { priorities: [], assignees: [], statuses: [], + status: -1, + type: -1, + priority: -1, + milestone: -1, + assignee: -1, } }, mounted() { const roadmapUrl = window.traq.base + window.traq.project_slug + "/roadmap.json" const componentsUrl = window.traq.base + "api/" + window.traq.project_slug + "/components" + const membersUrl = window.traq.base + "api/" + window.traq.project_slug + "/members" const statusesUrl = window.traq.base + "api/statuses" const prioritiesUrl = window.traq.base + "api/priorities" const typesUrl = window.traq.base + "api/types" - Promise.all([axios.get(roadmapUrl), axios.get(statusesUrl), axios.get(prioritiesUrl), axios.get(componentsUrl), axios.get(typesUrl)]).then( - ([roadmap, statuses, priorities, components, ticketTypes]) => { - this.milestones = - roadmap.data.map((data) => ({ - label: data.name, - value: data.slug, - })) ?? [] - - const open = - statuses.data - .filter((status) => status.status === 1) - .map((data) => ({ - label: data.name, - value: data.id, - })) ?? [] - - const closed = - statuses.data - .filter((status) => status.status === 0) - .map((data) => ({ - label: data.name, - value: data.id, - })) ?? [] - - this.statuses = { - Open: open, - Closed: closed, - } - - this.priorities = - priorities.data.map((data) => ({ + Promise.all([ + axios.get(roadmapUrl), + axios.get(statusesUrl), + axios.get(prioritiesUrl), + axios.get(componentsUrl), + axios.get(typesUrl), + axios.get(membersUrl), + ]).then(([roadmap, statuses, priorities, components, ticketTypes, members]) => { + this.milestones = + roadmap.data.map((data) => ({ + label: data.name, + value: data.id, + })) ?? [] + + const open = + statuses.data + .filter((status) => status.status === 1) + .map((data) => ({ label: data.name, value: data.id, })) ?? [] - this.components = - components.data.map((data) => ({ + const closed = + statuses.data + .filter((status) => status.status === 0) + .map((data) => ({ label: data.name, value: data.id, })) ?? [] - this.types = - ticketTypes.data.map((data) => ({ - label: data.name, - value: data.id, - })) ?? [] + this.statuses = { + Open: open, + Closed: closed, } - ) + + this.priorities = + priorities.data.map((data) => ({ + label: data.name, + value: data.id, + })) ?? [] + + this.components = + components.data.map((data) => ({ + label: data.name, + value: data.id, + })) ?? [] + + this.types = + ticketTypes.data.map((data) => ({ + label: data.name, + value: data.id, + })) ?? [] + + this.assignees = + members.data.map((data) => ({ + label: data.name, + value: data.id, + })) ?? [] + }) }, computed: { @@ -96,7 +113,7 @@ export default { diff --git a/traq-ui/ticket-listing/TicketListing.vue b/traq-ui/ticket-listing/TicketListing.vue index b44abd048..ac4c398d4 100644 --- a/traq-ui/ticket-listing/TicketListing.vue +++ b/traq-ui/ticket-listing/TicketListing.vue @@ -6,9 +6,10 @@ import TicketFilters from "./TicketFilters.vue" import TicketColumns from "./TicketColumns.vue" import MassActions from "./MassActions.vue" import { useAuthStore } from "../stores/auth" +import PaginationLinks from "../components/PaginationLinks.vue" export default { - components: { TicketFilters, TicketColumns, MassActions }, + components: { TicketFilters, TicketColumns, MassActions, PaginationLinks }, setup() { const auth = useAuthStore() @@ -22,10 +23,10 @@ export default { return { isLoading: true, tickets: [], - page: 1, - total_pages: 1, - sort_by: null, - sort_order: "desc", + currentPage: 1, + totalPages: 1, + sortBy: null, + sortOrder: "desc", filters: [], columns: ["ticket_id", "summary", "status", "type", "owner", "component", "milestone"], customFields: [], @@ -51,8 +52,8 @@ export default { let ticketsUrl = window.traq.base + window.traq.project_slug + "/tickets.json" // Add sorting options to URL - if (this.sort_by) { - ticketsUrl = `${ticketsUrl}?order_by=${this.sort_by}.${this.sort_order}` + if (this.sortBy) { + ticketsUrl = `${ticketsUrl}?order_by=${this.sortBy}.${this.sortOrder}` } // Add filters to URL @@ -61,7 +62,11 @@ export default { return `${filter.field}=` + (filter.condition ? "" : "!") + filter.values.join(",") }) - ticketsUrl = ticketsUrl + (this.sort_by ? "&" : "?") + filterBits.join("&") + ticketsUrl = ticketsUrl + (this.sortBy ? "&" : "?") + filterBits.join("&") + } + + if (this.currentPage > 1) { + ticketsUrl = ticketsUrl + (ticketsUrl.includes("?") ? "&" : "?") + `page=${this.currentPage}` } return ticketsUrl @@ -79,13 +84,13 @@ export default { getTickets(): void { axios.get(this.getTicketUrl).then((resp) => { this.tickets = resp.data.tickets - this.page = resp.data.page - this.total_pages = resp.data.total_pages + this.currentPage = resp.data.page + this.totalPages = resp.data.total_pages }) }, sortTickets(column): void { - this.sort_order = column !== this.sort_by || this.sort_order === "desc" ? "asc" : "desc" - this.sort_by = column + this.sortOrder = column !== this.sortBy || this.sortOrder === "desc" ? "asc" : "desc" + this.sortBy = column this.getTickets() this.updateUrl() }, @@ -134,8 +139,10 @@ export default { this.checkedTickets = [] }, changePage(page: number): void { - this.page = page + this.currentPage = page this.$refs["massActionsAllToggler"].checked = false + this.getTickets() + this.updateUrl() }, }, } @@ -155,101 +162,101 @@ export default { ID -