diff --git a/availabili.tf/package-lock.json b/availabili.tf/package-lock.json
index 2d7a15f..d65c053 100644
--- a/availabili.tf/package-lock.json
+++ b/availabili.tf/package-lock.json
@@ -13,7 +13,8 @@
"pinia": "^2.2.4",
"v-tooltip": "^2.1.3",
"vue": "^3.5.12",
- "vue-router": "^4.4.5"
+ "vue-router": "^4.4.5",
+ "vue-select": "^4.0.0-beta.6"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
@@ -31,6 +32,7 @@
"eslint-plugin-vue": "^9.29.0",
"jsdom": "^25.0.1",
"npm-run-all2": "^6.2.3",
+ "openapi-typescript-codegen": "^0.29.0",
"postcss": "^8.4.47",
"prettier": "^3.3.3",
"tailwindcss": "^3.4.14",
@@ -53,6 +55,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@apidevtools/json-schema-ref-parser": {
+ "version": "11.7.2",
+ "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.7.2.tgz",
+ "integrity": "sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jsdevtools/ono": "^7.1.3",
+ "@types/json-schema": "^7.0.15",
+ "js-yaml": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/philsturgeon"
+ }
+ },
"node_modules/@babel/helper-string-parser": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
@@ -868,6 +888,13 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@jsdevtools/ono": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
+ "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -2376,6 +2403,19 @@
"node": ">=6"
}
},
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
@@ -4059,6 +4099,28 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/handlebars": {
+ "version": "4.7.8",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
+ "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.2",
+ "source-map": "^0.6.1",
+ "wordwrap": "^1.0.0"
+ },
+ "bin": {
+ "handlebars": "bin/handlebars"
+ },
+ "engines": {
+ "node": ">=0.4.7"
+ },
+ "optionalDependencies": {
+ "uglify-js": "^3.1.4"
+ }
+ },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -5127,6 +5189,13 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/node-releases": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
@@ -5328,6 +5397,48 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/openapi-typescript-codegen": {
+ "version": "0.29.0",
+ "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.29.0.tgz",
+ "integrity": "sha512-/wC42PkD0LGjDTEULa/XiWQbv4E9NwLjwLjsaJ/62yOsoYhwvmBR31kPttn1DzQ2OlGe5stACcF/EIkZk43M6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@apidevtools/json-schema-ref-parser": "^11.5.4",
+ "camelcase": "^6.3.0",
+ "commander": "^12.0.0",
+ "fs-extra": "^11.2.0",
+ "handlebars": "^4.7.8"
+ },
+ "bin": {
+ "openapi": "bin/index.js"
+ }
+ },
+ "node_modules/openapi-typescript-codegen/node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/openapi-typescript-codegen/node_modules/fs-extra": {
+ "version": "11.2.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
+ "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -6308,7 +6419,6 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -6881,6 +6991,20 @@
}
}
},
+ "node_modules/uglify-js": {
+ "version": "3.19.3",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz",
+ "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "optional": true,
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
@@ -7316,6 +7440,15 @@
"vue": "^3.2.0"
}
},
+ "node_modules/vue-select": {
+ "version": "4.0.0-beta.6",
+ "resolved": "https://registry.npmjs.org/vue-select/-/vue-select-4.0.0-beta.6.tgz",
+ "integrity": "sha512-K+zrNBSpwMPhAxYLTCl56gaMrWZGgayoWCLqe5rWwkB8aUbAUh7u6sXjIR7v4ckp2WKC7zEEUY27g6h1MRsIHw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "vue": "3.x"
+ }
+ },
"node_modules/vue-tsc": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.1.6.tgz",
@@ -7447,6 +7580,13 @@
"node": ">=0.10.0"
}
},
+ "node_modules/wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/wrap-ansi": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
diff --git a/availabili.tf/package.json b/availabili.tf/package.json
index 705221a..ae1b006 100644
--- a/availabili.tf/package.json
+++ b/availabili.tf/package.json
@@ -11,7 +11,8 @@
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --fix",
- "format": "prettier --write src/"
+ "format": "prettier --write src/",
+ "openapi-generate": "openapi --input 'http://localhost:5000/apidoc/openapi.json' --output src/client --name AvailabilitfClient"
},
"dependencies": {
"axios": "^1.7.7",
@@ -19,7 +20,8 @@
"pinia": "^2.2.4",
"v-tooltip": "^2.1.3",
"vue": "^3.5.12",
- "vue-router": "^4.4.5"
+ "vue-router": "^4.4.5",
+ "vue-select": "^4.0.0-beta.6"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
@@ -37,6 +39,7 @@
"eslint-plugin-vue": "^9.29.0",
"jsdom": "^25.0.1",
"npm-run-all2": "^6.2.3",
+ "openapi-typescript-codegen": "^0.29.0",
"postcss": "^8.4.47",
"prettier": "^3.3.3",
"tailwindcss": "^3.4.14",
diff --git a/availabili.tf/src/App.vue b/availabili.tf/src/App.vue
index e87e5c0..02452b2 100644
--- a/availabili.tf/src/App.vue
+++ b/availabili.tf/src/App.vue
@@ -8,6 +8,7 @@ const baseUrl = window.location.origin;
-
+
+
+
diff --git a/availabili.tf/src/assets/base.css b/availabili.tf/src/assets/base.css
index 8de72dc..4659743 100644
--- a/availabili.tf/src/assets/base.css
+++ b/availabili.tf/src/assets/base.css
@@ -37,7 +37,8 @@
--flamingo: #f0c6c6;
--flamingo-transparent: #f0c6c655;
- --green: #a6e3a1;
+ --green: #40a02b;
+ --yellow: #df8e1d;
--lavender: #7287fd;
--accent: var(--lavender);
--accent-transparent-80: color-mix(in srgb, var(--accent), transparent 80%);
@@ -45,6 +46,10 @@
--accent-transparent: var(--accent-transparent-80);
--shadow: color-mix(in srgb, var(--text), transparent 50%);
+ --vs-selected-color: var(--text)!important;
+ --vs-dropdown-option--active-bg: var(--accent)!important;
+ --vs-dropdown-option--active-color: var(--base)!important;
+ --vs-border-color: var(--overlay-0)!important;
}
/* semantic color variables for this project */
diff --git a/availabili.tf/src/assets/main.css b/availabili.tf/src/assets/main.css
index 8497cda..74c736c 100644
--- a/availabili.tf/src/assets/main.css
+++ b/availabili.tf/src/assets/main.css
@@ -4,7 +4,6 @@
width: 100%;
max-width: 1280px;
margin: 0 auto;
- padding: 2rem;
font-weight: normal;
}
@@ -30,7 +29,7 @@ button {
background-color: var(--crust);
border: none;
padding: 10px 20px;
- border-radius: 8px;
+ border-radius: 4px;
font-family:
Inter,
-apple-system,
@@ -63,7 +62,7 @@ button:hover {
button.accent {
background-color: var(--accent);
color: var(--base);
- text-transform: uppercase;
+ /*text-transform: uppercase;*/
}
button.accent.dark {
@@ -88,6 +87,10 @@ button.transparent:hover {
background-color: var(--surface-0);
}
+button.small {
+ padding: 8px 16px;
+}
+
button[disabled] {
color: var(--overlay-0);
cursor: initial;
@@ -119,3 +122,31 @@ select {
.subtext {
color: var(--overlay-0);
}
+
+main {
+ padding: 2rem;
+}
+
+input {
+ padding: 6px 9px;
+ border: none;
+ /*outline: 1px solid var(--overlay-0);*/
+ border: 1px solid var(--overlay-0);
+ border-radius: 4px;
+ background-color: transparent;
+ /*font-size: 11pt;*/
+ font-size: 15px;
+ font-family:
+ Inter,
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ Roboto,
+ Oxygen,
+ Ubuntu,
+ Cantarell,
+ 'Fira Sans',
+ 'Droid Sans',
+ 'Helvetica Neue',
+ sans-serif;
+}
diff --git a/availabili.tf/src/assets/timezones.json b/availabili.tf/src/assets/timezones.json
new file mode 100644
index 0000000..29736bc
--- /dev/null
+++ b/availabili.tf/src/assets/timezones.json
@@ -0,0 +1,315 @@
+[
+ "Africa/Abidjan",
+ "Africa/Algiers",
+ "Africa/Bissau",
+ "Africa/Cairo",
+ "Africa/Casablanca",
+ "Africa/Ceuta",
+ "Africa/El_Aaiun",
+ "Africa/Johannesburg",
+ "Africa/Juba",
+ "Africa/Khartoum",
+ "Africa/Lagos",
+ "Africa/Maputo",
+ "Africa/Monrovia",
+ "Africa/Nairobi",
+ "Africa/Ndjamena",
+ "Africa/Sao_Tome",
+ "Africa/Tripoli",
+ "Africa/Tunis",
+ "Africa/Windhoek",
+ "America/Adak",
+ "America/Anchorage",
+ "America/Araguaina",
+ "America/Argentina/Buenos_Aires",
+ "America/Argentina/Catamarca",
+ "America/Argentina/Cordoba",
+ "America/Argentina/Jujuy",
+ "America/Argentina/La_Rioja",
+ "America/Argentina/Mendoza",
+ "America/Argentina/Rio_Gallegos",
+ "America/Argentina/Salta",
+ "America/Argentina/San_Juan",
+ "America/Argentina/San_Luis",
+ "America/Argentina/Tucuman",
+ "America/Argentina/Ushuaia",
+ "America/Asuncion",
+ "America/Bahia",
+ "America/Bahia_Banderas",
+ "America/Barbados",
+ "America/Belem",
+ "America/Belize",
+ "America/Boa_Vista",
+ "America/Bogota",
+ "America/Boise",
+ "America/Cambridge_Bay",
+ "America/Campo_Grande",
+ "America/Cancun",
+ "America/Caracas",
+ "America/Cayenne",
+ "America/Chicago",
+ "America/Chihuahua",
+ "America/Ciudad_Juarez",
+ "America/Costa_Rica",
+ "America/Cuiaba",
+ "America/Danmarkshavn",
+ "America/Dawson",
+ "America/Dawson_Creek",
+ "America/Denver",
+ "America/Detroit",
+ "America/Edmonton",
+ "America/Eirunepe",
+ "America/El_Salvador",
+ "America/Fort_Nelson",
+ "America/Fortaleza",
+ "America/Glace_Bay",
+ "America/Goose_Bay",
+ "America/Grand_Turk",
+ "America/Guatemala",
+ "America/Guayaquil",
+ "America/Guyana",
+ "America/Halifax",
+ "America/Havana",
+ "America/Hermosillo",
+ "America/Indiana/Indianapolis",
+ "America/Indiana/Knox",
+ "America/Indiana/Marengo",
+ "America/Indiana/Petersburg",
+ "America/Indiana/Tell_City",
+ "America/Indiana/Vevay",
+ "America/Indiana/Vincennes",
+ "America/Indiana/Winamac",
+ "America/Inuvik",
+ "America/Iqaluit",
+ "America/Jamaica",
+ "America/Juneau",
+ "America/Kentucky/Louisville",
+ "America/Kentucky/Monticello",
+ "America/La_Paz",
+ "America/Lima",
+ "America/Los_Angeles",
+ "America/Maceio",
+ "America/Managua",
+ "America/Manaus",
+ "America/Martinique",
+ "America/Matamoros",
+ "America/Mazatlan",
+ "America/Menominee",
+ "America/Merida",
+ "America/Metlakatla",
+ "America/Mexico_City",
+ "America/Miquelon",
+ "America/Moncton",
+ "America/Monterrey",
+ "America/Montevideo",
+ "America/New_York",
+ "America/Nome",
+ "America/Noronha",
+ "America/North_Dakota/Beulah",
+ "America/North_Dakota/Center",
+ "America/North_Dakota/New_Salem",
+ "America/Nuuk",
+ "America/Ojinaga",
+ "America/Panama",
+ "America/Paramaribo",
+ "America/Phoenix",
+ "America/Port-au-Prince",
+ "America/Porto_Velho",
+ "America/Puerto_Rico",
+ "America/Punta_Arenas",
+ "America/Rankin_Inlet",
+ "America/Recife",
+ "America/Regina",
+ "America/Resolute",
+ "America/Rio_Branco",
+ "America/Santarem",
+ "America/Santiago",
+ "America/Santo_Domingo",
+ "America/Sao_Paulo",
+ "America/Scoresbysund",
+ "America/Sitka",
+ "America/St_Johns",
+ "America/Swift_Current",
+ "America/Tegucigalpa",
+ "America/Thule",
+ "America/Tijuana",
+ "America/Toronto",
+ "America/Vancouver",
+ "America/Whitehorse",
+ "America/Winnipeg",
+ "America/Yakutat",
+ "Antarctica/Casey",
+ "Antarctica/Davis",
+ "Antarctica/Macquarie",
+ "Antarctica/Mawson",
+ "Antarctica/Palmer",
+ "Antarctica/Rothera",
+ "Antarctica/Troll",
+ "Antarctica/Vostok",
+ "Asia/Almaty",
+ "Asia/Amman",
+ "Asia/Anadyr",
+ "Asia/Aqtau",
+ "Asia/Aqtobe",
+ "Asia/Ashgabat",
+ "Asia/Atyrau",
+ "Asia/Baghdad",
+ "Asia/Baku",
+ "Asia/Bangkok",
+ "Asia/Barnaul",
+ "Asia/Beirut",
+ "Asia/Bishkek",
+ "Asia/Chita",
+ "Asia/Colombo",
+ "Asia/Damascus",
+ "Asia/Dhaka",
+ "Asia/Dili",
+ "Asia/Dubai",
+ "Asia/Dushanbe",
+ "Asia/Famagusta",
+ "Asia/Gaza",
+ "Asia/Hebron",
+ "Asia/Ho_Chi_Minh",
+ "Asia/Hong_Kong",
+ "Asia/Hovd",
+ "Asia/Irkutsk",
+ "Asia/Jakarta",
+ "Asia/Jayapura",
+ "Asia/Jerusalem",
+ "Asia/Kabul",
+ "Asia/Kamchatka",
+ "Asia/Karachi",
+ "Asia/Kathmandu",
+ "Asia/Khandyga",
+ "Asia/Kolkata",
+ "Asia/Krasnoyarsk",
+ "Asia/Kuching",
+ "Asia/Macau",
+ "Asia/Magadan",
+ "Asia/Makassar",
+ "Asia/Manila",
+ "Asia/Nicosia",
+ "Asia/Novokuznetsk",
+ "Asia/Novosibirsk",
+ "Asia/Omsk",
+ "Asia/Oral",
+ "Asia/Pontianak",
+ "Asia/Pyongyang",
+ "Asia/Qatar",
+ "Asia/Qostanay",
+ "Asia/Qyzylorda",
+ "Asia/Riyadh",
+ "Asia/Sakhalin",
+ "Asia/Samarkand",
+ "Asia/Seoul",
+ "Asia/Shanghai",
+ "Asia/Singapore",
+ "Asia/Srednekolymsk",
+ "Asia/Taipei",
+ "Asia/Tashkent",
+ "Asia/Tbilisi",
+ "Asia/Tehran",
+ "Asia/Thimphu",
+ "Asia/Tokyo",
+ "Asia/Tomsk",
+ "Asia/Ulaanbaatar",
+ "Asia/Urumqi",
+ "Asia/Ust-Nera",
+ "Asia/Vladivostok",
+ "Asia/Yakutsk",
+ "Asia/Yangon",
+ "Asia/Yekaterinburg",
+ "Asia/Yerevan",
+ "Atlantic/Azores",
+ "Atlantic/Bermuda",
+ "Atlantic/Canary",
+ "Atlantic/Cape_Verde",
+ "Atlantic/Faroe",
+ "Atlantic/Madeira",
+ "Atlantic/South_Georgia",
+ "Atlantic/Stanley",
+ "Australia/Adelaide",
+ "Australia/Brisbane",
+ "Australia/Broken_Hill",
+ "Australia/Darwin",
+ "Australia/Eucla",
+ "Australia/Hobart",
+ "Australia/Lindeman",
+ "Australia/Lord_Howe",
+ "Australia/Melbourne",
+ "Australia/Perth",
+ "Australia/Sydney",
+ "Etc/UTC",
+ "Europe/Andorra",
+ "Europe/Astrakhan",
+ "Europe/Athens",
+ "Europe/Belgrade",
+ "Europe/Berlin",
+ "Europe/Brussels",
+ "Europe/Bucharest",
+ "Europe/Budapest",
+ "Europe/Chisinau",
+ "Europe/Dublin",
+ "Europe/Gibraltar",
+ "Europe/Helsinki",
+ "Europe/Istanbul",
+ "Europe/Kaliningrad",
+ "Europe/Kirov",
+ "Europe/Kyiv",
+ "Europe/Lisbon",
+ "Europe/London",
+ "Europe/Madrid",
+ "Europe/Malta",
+ "Europe/Minsk",
+ "Europe/Moscow",
+ "Europe/Paris",
+ "Europe/Prague",
+ "Europe/Riga",
+ "Europe/Rome",
+ "Europe/Samara",
+ "Europe/Saratov",
+ "Europe/Simferopol",
+ "Europe/Sofia",
+ "Europe/Tallinn",
+ "Europe/Tirane",
+ "Europe/Ulyanovsk",
+ "Europe/Vienna",
+ "Europe/Vilnius",
+ "Europe/Volgograd",
+ "Europe/Warsaw",
+ "Europe/Zurich",
+ "Factory",
+ "Indian/Chagos",
+ "Indian/Maldives",
+ "Indian/Mauritius",
+ "Pacific/Apia",
+ "Pacific/Auckland",
+ "Pacific/Bougainville",
+ "Pacific/Chatham",
+ "Pacific/Easter",
+ "Pacific/Efate",
+ "Pacific/Fakaofo",
+ "Pacific/Fiji",
+ "Pacific/Galapagos",
+ "Pacific/Gambier",
+ "Pacific/Guadalcanal",
+ "Pacific/Guam",
+ "Pacific/Honolulu",
+ "Pacific/Kanton",
+ "Pacific/Kiritimati",
+ "Pacific/Kosrae",
+ "Pacific/Kwajalein",
+ "Pacific/Marquesas",
+ "Pacific/Nauru",
+ "Pacific/Niue",
+ "Pacific/Norfolk",
+ "Pacific/Noumea",
+ "Pacific/Pago_Pago",
+ "Pacific/Palau",
+ "Pacific/Pitcairn",
+ "Pacific/Port_Moresby",
+ "Pacific/Rarotonga",
+ "Pacific/Tahiti",
+ "Pacific/Tarawa",
+ "Pacific/Tongatapu"
+]
diff --git a/availabili.tf/src/client/AvailabilitfClient.ts b/availabili.tf/src/client/AvailabilitfClient.ts
new file mode 100644
index 0000000..805c515
--- /dev/null
+++ b/availabili.tf/src/client/AvailabilitfClient.ts
@@ -0,0 +1,28 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { BaseHttpRequest } from './core/BaseHttpRequest';
+import type { OpenAPIConfig } from './core/OpenAPI';
+import { FetchHttpRequest } from './core/FetchHttpRequest';
+import { DefaultService } from './services/DefaultService';
+type HttpRequestConstructor = new (config: OpenAPIConfig) => BaseHttpRequest;
+export class AvailabilitfClient {
+ public readonly default: DefaultService;
+ public readonly request: BaseHttpRequest;
+ constructor(config?: Partial, HttpRequest: HttpRequestConstructor = FetchHttpRequest) {
+ this.request = new HttpRequest({
+ BASE: config?.BASE ?? '',
+ VERSION: config?.VERSION ?? '0.1.0',
+ WITH_CREDENTIALS: config?.WITH_CREDENTIALS ?? false,
+ CREDENTIALS: config?.CREDENTIALS ?? 'include',
+ TOKEN: config?.TOKEN,
+ USERNAME: config?.USERNAME,
+ PASSWORD: config?.PASSWORD,
+ HEADERS: config?.HEADERS,
+ ENCODE_PATH: config?.ENCODE_PATH,
+ });
+ this.default = new DefaultService(this.request);
+ }
+}
+
diff --git a/availabili.tf/src/client/core/ApiError.ts b/availabili.tf/src/client/core/ApiError.ts
new file mode 100644
index 0000000..ec7b16a
--- /dev/null
+++ b/availabili.tf/src/client/core/ApiError.ts
@@ -0,0 +1,25 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiRequestOptions } from './ApiRequestOptions';
+import type { ApiResult } from './ApiResult';
+
+export class ApiError extends Error {
+ public readonly url: string;
+ public readonly status: number;
+ public readonly statusText: string;
+ public readonly body: any;
+ public readonly request: ApiRequestOptions;
+
+ constructor(request: ApiRequestOptions, response: ApiResult, message: string) {
+ super(message);
+
+ this.name = 'ApiError';
+ this.url = response.url;
+ this.status = response.status;
+ this.statusText = response.statusText;
+ this.body = response.body;
+ this.request = request;
+ }
+}
diff --git a/availabili.tf/src/client/core/ApiRequestOptions.ts b/availabili.tf/src/client/core/ApiRequestOptions.ts
new file mode 100644
index 0000000..93143c3
--- /dev/null
+++ b/availabili.tf/src/client/core/ApiRequestOptions.ts
@@ -0,0 +1,17 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ApiRequestOptions = {
+ readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH';
+ readonly url: string;
+ readonly path?: Record;
+ readonly cookies?: Record;
+ readonly headers?: Record;
+ readonly query?: Record;
+ readonly formData?: Record;
+ readonly body?: any;
+ readonly mediaType?: string;
+ readonly responseHeader?: string;
+ readonly errors?: Record;
+};
diff --git a/availabili.tf/src/client/core/ApiResult.ts b/availabili.tf/src/client/core/ApiResult.ts
new file mode 100644
index 0000000..ee1126e
--- /dev/null
+++ b/availabili.tf/src/client/core/ApiResult.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ApiResult = {
+ readonly url: string;
+ readonly ok: boolean;
+ readonly status: number;
+ readonly statusText: string;
+ readonly body: any;
+};
diff --git a/availabili.tf/src/client/core/BaseHttpRequest.ts b/availabili.tf/src/client/core/BaseHttpRequest.ts
new file mode 100644
index 0000000..04aad6a
--- /dev/null
+++ b/availabili.tf/src/client/core/BaseHttpRequest.ts
@@ -0,0 +1,14 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiRequestOptions } from './ApiRequestOptions';
+import type { CancelablePromise } from './CancelablePromise';
+import type { OpenAPIConfig } from './OpenAPI';
+
+export abstract class BaseHttpRequest {
+
+ constructor(public readonly config: OpenAPIConfig) {}
+
+ public abstract request(options: ApiRequestOptions): CancelablePromise;
+}
diff --git a/availabili.tf/src/client/core/CancelablePromise.ts b/availabili.tf/src/client/core/CancelablePromise.ts
new file mode 100644
index 0000000..d70de92
--- /dev/null
+++ b/availabili.tf/src/client/core/CancelablePromise.ts
@@ -0,0 +1,131 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export class CancelError extends Error {
+
+ constructor(message: string) {
+ super(message);
+ this.name = 'CancelError';
+ }
+
+ public get isCancelled(): boolean {
+ return true;
+ }
+}
+
+export interface OnCancel {
+ readonly isResolved: boolean;
+ readonly isRejected: boolean;
+ readonly isCancelled: boolean;
+
+ (cancelHandler: () => void): void;
+}
+
+export class CancelablePromise implements Promise {
+ #isResolved: boolean;
+ #isRejected: boolean;
+ #isCancelled: boolean;
+ readonly #cancelHandlers: (() => void)[];
+ readonly #promise: Promise;
+ #resolve?: (value: T | PromiseLike) => void;
+ #reject?: (reason?: any) => void;
+
+ constructor(
+ executor: (
+ resolve: (value: T | PromiseLike) => void,
+ reject: (reason?: any) => void,
+ onCancel: OnCancel
+ ) => void
+ ) {
+ this.#isResolved = false;
+ this.#isRejected = false;
+ this.#isCancelled = false;
+ this.#cancelHandlers = [];
+ this.#promise = new Promise((resolve, reject) => {
+ this.#resolve = resolve;
+ this.#reject = reject;
+
+ const onResolve = (value: T | PromiseLike): void => {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#isResolved = true;
+ if (this.#resolve) this.#resolve(value);
+ };
+
+ const onReject = (reason?: any): void => {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#isRejected = true;
+ if (this.#reject) this.#reject(reason);
+ };
+
+ const onCancel = (cancelHandler: () => void): void => {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#cancelHandlers.push(cancelHandler);
+ };
+
+ Object.defineProperty(onCancel, 'isResolved', {
+ get: (): boolean => this.#isResolved,
+ });
+
+ Object.defineProperty(onCancel, 'isRejected', {
+ get: (): boolean => this.#isRejected,
+ });
+
+ Object.defineProperty(onCancel, 'isCancelled', {
+ get: (): boolean => this.#isCancelled,
+ });
+
+ return executor(onResolve, onReject, onCancel as OnCancel);
+ });
+ }
+
+ get [Symbol.toStringTag]() {
+ return "Cancellable Promise";
+ }
+
+ public then(
+ onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null,
+ onRejected?: ((reason: any) => TResult2 | PromiseLike) | null
+ ): Promise {
+ return this.#promise.then(onFulfilled, onRejected);
+ }
+
+ public catch(
+ onRejected?: ((reason: any) => TResult | PromiseLike) | null
+ ): Promise {
+ return this.#promise.catch(onRejected);
+ }
+
+ public finally(onFinally?: (() => void) | null): Promise {
+ return this.#promise.finally(onFinally);
+ }
+
+ public cancel(): void {
+ if (this.#isResolved || this.#isRejected || this.#isCancelled) {
+ return;
+ }
+ this.#isCancelled = true;
+ if (this.#cancelHandlers.length) {
+ try {
+ for (const cancelHandler of this.#cancelHandlers) {
+ cancelHandler();
+ }
+ } catch (error) {
+ console.warn('Cancellation threw an error', error);
+ return;
+ }
+ }
+ this.#cancelHandlers.length = 0;
+ if (this.#reject) this.#reject(new CancelError('Request aborted'));
+ }
+
+ public get isCancelled(): boolean {
+ return this.#isCancelled;
+ }
+}
diff --git a/availabili.tf/src/client/core/FetchHttpRequest.ts b/availabili.tf/src/client/core/FetchHttpRequest.ts
new file mode 100644
index 0000000..e58111a
--- /dev/null
+++ b/availabili.tf/src/client/core/FetchHttpRequest.ts
@@ -0,0 +1,26 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiRequestOptions } from './ApiRequestOptions';
+import { BaseHttpRequest } from './BaseHttpRequest';
+import type { CancelablePromise } from './CancelablePromise';
+import type { OpenAPIConfig } from './OpenAPI';
+import { request as __request } from './request';
+
+export class FetchHttpRequest extends BaseHttpRequest {
+
+ constructor(config: OpenAPIConfig) {
+ super(config);
+ }
+
+ /**
+ * Request method
+ * @param options The request options from the service
+ * @returns CancelablePromise
+ * @throws ApiError
+ */
+ public override request(options: ApiRequestOptions): CancelablePromise {
+ return __request(this.config, options);
+ }
+}
diff --git a/availabili.tf/src/client/core/OpenAPI.ts b/availabili.tf/src/client/core/OpenAPI.ts
new file mode 100644
index 0000000..0cfae5d
--- /dev/null
+++ b/availabili.tf/src/client/core/OpenAPI.ts
@@ -0,0 +1,32 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ApiRequestOptions } from './ApiRequestOptions';
+
+type Resolver = (options: ApiRequestOptions) => Promise;
+type Headers = Record;
+
+export type OpenAPIConfig = {
+ BASE: string;
+ VERSION: string;
+ WITH_CREDENTIALS: boolean;
+ CREDENTIALS: 'include' | 'omit' | 'same-origin';
+ TOKEN?: string | Resolver | undefined;
+ USERNAME?: string | Resolver | undefined;
+ PASSWORD?: string | Resolver | undefined;
+ HEADERS?: Headers | Resolver | undefined;
+ ENCODE_PATH?: ((path: string) => string) | undefined;
+};
+
+export const OpenAPI: OpenAPIConfig = {
+ BASE: '',
+ VERSION: '0.1.0',
+ WITH_CREDENTIALS: false,
+ CREDENTIALS: 'include',
+ TOKEN: undefined,
+ USERNAME: undefined,
+ PASSWORD: undefined,
+ HEADERS: undefined,
+ ENCODE_PATH: undefined,
+};
diff --git a/availabili.tf/src/client/core/request.ts b/availabili.tf/src/client/core/request.ts
new file mode 100644
index 0000000..f83d711
--- /dev/null
+++ b/availabili.tf/src/client/core/request.ts
@@ -0,0 +1,322 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import { ApiError } from './ApiError';
+import type { ApiRequestOptions } from './ApiRequestOptions';
+import type { ApiResult } from './ApiResult';
+import { CancelablePromise } from './CancelablePromise';
+import type { OnCancel } from './CancelablePromise';
+import type { OpenAPIConfig } from './OpenAPI';
+
+export const isDefined = (value: T | null | undefined): value is Exclude => {
+ return value !== undefined && value !== null;
+};
+
+export const isString = (value: any): value is string => {
+ return typeof value === 'string';
+};
+
+export const isStringWithValue = (value: any): value is string => {
+ return isString(value) && value !== '';
+};
+
+export const isBlob = (value: any): value is Blob => {
+ return (
+ typeof value === 'object' &&
+ typeof value.type === 'string' &&
+ typeof value.stream === 'function' &&
+ typeof value.arrayBuffer === 'function' &&
+ typeof value.constructor === 'function' &&
+ typeof value.constructor.name === 'string' &&
+ /^(Blob|File)$/.test(value.constructor.name) &&
+ /^(Blob|File)$/.test(value[Symbol.toStringTag])
+ );
+};
+
+export const isFormData = (value: any): value is FormData => {
+ return value instanceof FormData;
+};
+
+export const base64 = (str: string): string => {
+ try {
+ return btoa(str);
+ } catch (err) {
+ // @ts-ignore
+ return Buffer.from(str).toString('base64');
+ }
+};
+
+export const getQueryString = (params: Record): string => {
+ const qs: string[] = [];
+
+ const append = (key: string, value: any) => {
+ qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
+ };
+
+ const process = (key: string, value: any) => {
+ if (isDefined(value)) {
+ if (Array.isArray(value)) {
+ value.forEach(v => {
+ process(key, v);
+ });
+ } else if (typeof value === 'object') {
+ Object.entries(value).forEach(([k, v]) => {
+ process(`${key}[${k}]`, v);
+ });
+ } else {
+ append(key, value);
+ }
+ }
+ };
+
+ Object.entries(params).forEach(([key, value]) => {
+ process(key, value);
+ });
+
+ if (qs.length > 0) {
+ return `?${qs.join('&')}`;
+ }
+
+ return '';
+};
+
+const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => {
+ const encoder = config.ENCODE_PATH || encodeURI;
+
+ const path = options.url
+ .replace('{api-version}', config.VERSION)
+ .replace(/{(.*?)}/g, (substring: string, group: string) => {
+ if (options.path?.hasOwnProperty(group)) {
+ return encoder(String(options.path[group]));
+ }
+ return substring;
+ });
+
+ const url = `${config.BASE}${path}`;
+ if (options.query) {
+ return `${url}${getQueryString(options.query)}`;
+ }
+ return url;
+};
+
+export const getFormData = (options: ApiRequestOptions): FormData | undefined => {
+ if (options.formData) {
+ const formData = new FormData();
+
+ const process = (key: string, value: any) => {
+ if (isString(value) || isBlob(value)) {
+ formData.append(key, value);
+ } else {
+ formData.append(key, JSON.stringify(value));
+ }
+ };
+
+ Object.entries(options.formData)
+ .filter(([_, value]) => isDefined(value))
+ .forEach(([key, value]) => {
+ if (Array.isArray(value)) {
+ value.forEach(v => process(key, v));
+ } else {
+ process(key, value);
+ }
+ });
+
+ return formData;
+ }
+ return undefined;
+};
+
+type Resolver = (options: ApiRequestOptions) => Promise;
+
+export const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => {
+ if (typeof resolver === 'function') {
+ return (resolver as Resolver)(options);
+ }
+ return resolver;
+};
+
+export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise => {
+ const [token, username, password, additionalHeaders] = await Promise.all([
+ resolve(options, config.TOKEN),
+ resolve(options, config.USERNAME),
+ resolve(options, config.PASSWORD),
+ resolve(options, config.HEADERS),
+ ]);
+
+ const headers = Object.entries({
+ Accept: 'application/json',
+ ...additionalHeaders,
+ ...options.headers,
+ })
+ .filter(([_, value]) => isDefined(value))
+ .reduce((headers, [key, value]) => ({
+ ...headers,
+ [key]: String(value),
+ }), {} as Record);
+
+ if (isStringWithValue(token)) {
+ headers['Authorization'] = `Bearer ${token}`;
+ }
+
+ if (isStringWithValue(username) && isStringWithValue(password)) {
+ const credentials = base64(`${username}:${password}`);
+ headers['Authorization'] = `Basic ${credentials}`;
+ }
+
+ if (options.body !== undefined) {
+ if (options.mediaType) {
+ headers['Content-Type'] = options.mediaType;
+ } else if (isBlob(options.body)) {
+ headers['Content-Type'] = options.body.type || 'application/octet-stream';
+ } else if (isString(options.body)) {
+ headers['Content-Type'] = 'text/plain';
+ } else if (!isFormData(options.body)) {
+ headers['Content-Type'] = 'application/json';
+ }
+ }
+
+ return new Headers(headers);
+};
+
+export const getRequestBody = (options: ApiRequestOptions): any => {
+ if (options.body !== undefined) {
+ if (options.mediaType?.includes('/json')) {
+ return JSON.stringify(options.body)
+ } else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) {
+ return options.body;
+ } else {
+ return JSON.stringify(options.body);
+ }
+ }
+ return undefined;
+};
+
+export const sendRequest = async (
+ config: OpenAPIConfig,
+ options: ApiRequestOptions,
+ url: string,
+ body: any,
+ formData: FormData | undefined,
+ headers: Headers,
+ onCancel: OnCancel
+): Promise => {
+ const controller = new AbortController();
+
+ const request: RequestInit = {
+ headers,
+ body: body ?? formData,
+ method: options.method,
+ signal: controller.signal,
+ };
+
+ if (config.WITH_CREDENTIALS) {
+ request.credentials = config.CREDENTIALS;
+ }
+
+ onCancel(() => controller.abort());
+
+ return await fetch(url, request);
+};
+
+export const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => {
+ if (responseHeader) {
+ const content = response.headers.get(responseHeader);
+ if (isString(content)) {
+ return content;
+ }
+ }
+ return undefined;
+};
+
+export const getResponseBody = async (response: Response): Promise => {
+ if (response.status !== 204) {
+ try {
+ const contentType = response.headers.get('Content-Type');
+ if (contentType) {
+ const jsonTypes = ['application/json', 'application/problem+json']
+ const isJSON = jsonTypes.some(type => contentType.toLowerCase().startsWith(type));
+ if (isJSON) {
+ return await response.json();
+ } else {
+ return await response.text();
+ }
+ }
+ } catch (error) {
+ console.error(error);
+ }
+ }
+ return undefined;
+};
+
+export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => {
+ const errors: Record = {
+ 400: 'Bad Request',
+ 401: 'Unauthorized',
+ 403: 'Forbidden',
+ 404: 'Not Found',
+ 500: 'Internal Server Error',
+ 502: 'Bad Gateway',
+ 503: 'Service Unavailable',
+ ...options.errors,
+ }
+
+ const error = errors[result.status];
+ if (error) {
+ throw new ApiError(options, result, error);
+ }
+
+ if (!result.ok) {
+ const errorStatus = result.status ?? 'unknown';
+ const errorStatusText = result.statusText ?? 'unknown';
+ const errorBody = (() => {
+ try {
+ return JSON.stringify(result.body, null, 2);
+ } catch (e) {
+ return undefined;
+ }
+ })();
+
+ throw new ApiError(options, result,
+ `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}`
+ );
+ }
+};
+
+/**
+ * Request method
+ * @param config The OpenAPI configuration object
+ * @param options The request options from the service
+ * @returns CancelablePromise
+ * @throws ApiError
+ */
+export const request = (config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise => {
+ return new CancelablePromise(async (resolve, reject, onCancel) => {
+ try {
+ const url = getUrl(config, options);
+ const formData = getFormData(options);
+ const body = getRequestBody(options);
+ const headers = await getHeaders(config, options);
+
+ if (!onCancel.isCancelled) {
+ const response = await sendRequest(config, options, url, body, formData, headers, onCancel);
+ const responseBody = await getResponseBody(response);
+ const responseHeader = getResponseHeader(response, options.responseHeader);
+
+ const result: ApiResult = {
+ url,
+ ok: response.ok,
+ status: response.status,
+ statusText: response.statusText,
+ body: responseHeader ?? responseBody,
+ };
+
+ catchErrorCodes(options, result);
+
+ resolve(result.body);
+ }
+ } catch (error) {
+ reject(error);
+ }
+ });
+};
diff --git a/availabili.tf/src/client/index.ts b/availabili.tf/src/client/index.ts
new file mode 100644
index 0000000..c486b9d
--- /dev/null
+++ b/availabili.tf/src/client/index.ts
@@ -0,0 +1,29 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export { AvailabilitfClient } from './AvailabilitfClient';
+
+export { ApiError } from './core/ApiError';
+export { BaseHttpRequest } from './core/BaseHttpRequest';
+export { CancelablePromise, CancelError } from './core/CancelablePromise';
+export { OpenAPI } from './core/OpenAPI';
+export type { OpenAPIConfig } from './core/OpenAPI';
+
+export type { AddPlayerJson } from './models/AddPlayerJson';
+export type { CreateTeamJson } from './models/CreateTeamJson';
+export type { PutScheduleForm } from './models/PutScheduleForm';
+export type { RoleSchema } from './models/RoleSchema';
+export { TeamRole } from './models/TeamRole';
+export type { TeamSchema } from './models/TeamSchema';
+export type { ValidationError } from './models/ValidationError';
+export type { ValidationErrorElement } from './models/ValidationErrorElement';
+export type { ViewAvailablePlayersForm } from './models/ViewAvailablePlayersForm';
+export type { ViewScheduleForm } from './models/ViewScheduleForm';
+export type { ViewScheduleResponse } from './models/ViewScheduleResponse';
+export type { ViewTeamMembersResponse } from './models/ViewTeamMembersResponse';
+export type { ViewTeamMembersResponseList } from './models/ViewTeamMembersResponseList';
+export type { ViewTeamResponse } from './models/ViewTeamResponse';
+export type { ViewTeamsResponse } from './models/ViewTeamsResponse';
+
+export { DefaultService } from './services/DefaultService';
diff --git a/availabili.tf/src/client/models/AddPlayerJson.ts b/availabili.tf/src/client/models/AddPlayerJson.ts
new file mode 100644
index 0000000..21bf091
--- /dev/null
+++ b/availabili.tf/src/client/models/AddPlayerJson.ts
@@ -0,0 +1,10 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { TeamRole } from './TeamRole';
+export type AddPlayerJson = {
+ isTeamLeader?: boolean;
+ teamRole?: TeamRole;
+};
+
diff --git a/availabili.tf/src/client/models/CreateTeamJson.ts b/availabili.tf/src/client/models/CreateTeamJson.ts
new file mode 100644
index 0000000..4308733
--- /dev/null
+++ b/availabili.tf/src/client/models/CreateTeamJson.ts
@@ -0,0 +1,10 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type CreateTeamJson = {
+ discordWebhookUrl?: string;
+ leagueTimezone: string;
+ teamName: string;
+};
+
diff --git a/availabili.tf/src/client/models/PutScheduleForm.ts b/availabili.tf/src/client/models/PutScheduleForm.ts
new file mode 100644
index 0000000..29cc297
--- /dev/null
+++ b/availabili.tf/src/client/models/PutScheduleForm.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type PutScheduleForm = {
+ availability: Array;
+ teamId: number;
+ windowSizeDays?: number;
+ windowStart: string;
+};
+
diff --git a/availabili.tf/src/client/models/RoleSchema.ts b/availabili.tf/src/client/models/RoleSchema.ts
new file mode 100644
index 0000000..4ef837c
--- /dev/null
+++ b/availabili.tf/src/client/models/RoleSchema.ts
@@ -0,0 +1,9 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type RoleSchema = {
+ isMain: boolean;
+ role: string;
+};
+
diff --git a/availabili.tf/src/client/models/TeamRole.ts b/availabili.tf/src/client/models/TeamRole.ts
new file mode 100644
index 0000000..6bf79f5
--- /dev/null
+++ b/availabili.tf/src/client/models/TeamRole.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * An enumeration.
+ */
+export enum TeamRole {
+ '_0' = 0,
+ '_1' = 1,
+}
diff --git a/availabili.tf/src/client/models/TeamSchema.ts b/availabili.tf/src/client/models/TeamSchema.ts
new file mode 100644
index 0000000..0df9a2a
--- /dev/null
+++ b/availabili.tf/src/client/models/TeamSchema.ts
@@ -0,0 +1,10 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type TeamSchema = {
+ discordWebhookUrl?: string;
+ id: number;
+ teamName: string;
+};
+
diff --git a/availabili.tf/src/client/models/ValidationError.ts b/availabili.tf/src/client/models/ValidationError.ts
new file mode 100644
index 0000000..95249e2
--- /dev/null
+++ b/availabili.tf/src/client/models/ValidationError.ts
@@ -0,0 +1,9 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ValidationErrorElement } from './ValidationErrorElement';
+/**
+ * Model of a validation error response.
+ */
+export type ValidationError = Array;
diff --git a/availabili.tf/src/client/models/ValidationErrorElement.ts b/availabili.tf/src/client/models/ValidationErrorElement.ts
new file mode 100644
index 0000000..8867465
--- /dev/null
+++ b/availabili.tf/src/client/models/ValidationErrorElement.ts
@@ -0,0 +1,14 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Model of a validation error response element.
+ */
+export type ValidationErrorElement = {
+ ctx?: Record;
+ loc: Array;
+ msg: string;
+ type: string;
+};
+
diff --git a/availabili.tf/src/client/models/ViewAvailablePlayersForm.ts b/availabili.tf/src/client/models/ViewAvailablePlayersForm.ts
new file mode 100644
index 0000000..d46cd46
--- /dev/null
+++ b/availabili.tf/src/client/models/ViewAvailablePlayersForm.ts
@@ -0,0 +1,9 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ViewAvailablePlayersForm = {
+ startTime: string;
+ teamId: number;
+};
+
diff --git a/availabili.tf/src/client/models/ViewScheduleForm.ts b/availabili.tf/src/client/models/ViewScheduleForm.ts
new file mode 100644
index 0000000..824fdca
--- /dev/null
+++ b/availabili.tf/src/client/models/ViewScheduleForm.ts
@@ -0,0 +1,10 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ViewScheduleForm = {
+ teamId: number;
+ windowSizeDays?: number;
+ windowStart: string;
+};
+
diff --git a/availabili.tf/src/client/models/ViewScheduleResponse.ts b/availabili.tf/src/client/models/ViewScheduleResponse.ts
new file mode 100644
index 0000000..48e1b05
--- /dev/null
+++ b/availabili.tf/src/client/models/ViewScheduleResponse.ts
@@ -0,0 +1,8 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+export type ViewScheduleResponse = {
+ availability: Array;
+};
+
diff --git a/availabili.tf/src/client/models/ViewTeamMembersResponse.ts b/availabili.tf/src/client/models/ViewTeamMembersResponse.ts
new file mode 100644
index 0000000..bcae8d5
--- /dev/null
+++ b/availabili.tf/src/client/models/ViewTeamMembersResponse.ts
@@ -0,0 +1,11 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { RoleSchema } from './RoleSchema';
+export type ViewTeamMembersResponse = {
+ roles: Array;
+ steamId: string;
+ username: string;
+};
+
diff --git a/availabili.tf/src/client/models/ViewTeamMembersResponseList.ts b/availabili.tf/src/client/models/ViewTeamMembersResponseList.ts
new file mode 100644
index 0000000..e1f3a29
--- /dev/null
+++ b/availabili.tf/src/client/models/ViewTeamMembersResponseList.ts
@@ -0,0 +1,6 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { ViewTeamMembersResponse } from './ViewTeamMembersResponse';
+export type ViewTeamMembersResponseList = Array;
diff --git a/availabili.tf/src/client/models/ViewTeamResponse.ts b/availabili.tf/src/client/models/ViewTeamResponse.ts
new file mode 100644
index 0000000..27faae3
--- /dev/null
+++ b/availabili.tf/src/client/models/ViewTeamResponse.ts
@@ -0,0 +1,9 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { TeamSchema } from './TeamSchema';
+export type ViewTeamResponse = {
+ team: TeamSchema;
+};
+
diff --git a/availabili.tf/src/client/models/ViewTeamsResponse.ts b/availabili.tf/src/client/models/ViewTeamsResponse.ts
new file mode 100644
index 0000000..2977326
--- /dev/null
+++ b/availabili.tf/src/client/models/ViewTeamsResponse.ts
@@ -0,0 +1,9 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { TeamSchema } from './TeamSchema';
+export type ViewTeamsResponse = {
+ teams: Array;
+};
+
diff --git a/availabili.tf/src/client/services/DefaultService.ts b/availabili.tf/src/client/services/DefaultService.ts
new file mode 100644
index 0000000..bea30f0
--- /dev/null
+++ b/availabili.tf/src/client/services/DefaultService.ts
@@ -0,0 +1,264 @@
+/* generated using openapi-typescript-codegen -- do not edit */
+/* istanbul ignore file */
+/* tslint:disable */
+/* eslint-disable */
+import type { AddPlayerJson } from '../models/AddPlayerJson';
+import type { CreateTeamJson } from '../models/CreateTeamJson';
+import type { PutScheduleForm } from '../models/PutScheduleForm';
+import type { ViewScheduleResponse } from '../models/ViewScheduleResponse';
+import type { ViewTeamMembersResponseList } from '../models/ViewTeamMembersResponseList';
+import type { ViewTeamResponse } from '../models/ViewTeamResponse';
+import type { ViewTeamsResponse } from '../models/ViewTeamsResponse';
+import type { CancelablePromise } from '../core/CancelablePromise';
+import type { BaseHttpRequest } from '../core/BaseHttpRequest';
+export class DefaultService {
+ constructor(public readonly httpRequest: BaseHttpRequest) {}
+ /**
+ * debug_set_cookie
+ * @returns void
+ * @throws ApiError
+ */
+ public getApiDebugSetCookie(): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'GET',
+ url: '/api/debug/set-cookie',
+ });
+ }
+ /**
+ * debug_set_cookie
+ * @returns void
+ * @throws ApiError
+ */
+ public postApiDebugSetCookie(): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'POST',
+ url: '/api/debug/set-cookie',
+ });
+ }
+ /**
+ * logout
+ * @returns void
+ * @throws ApiError
+ */
+ public deleteApiLogin(): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'DELETE',
+ url: '/api/login/',
+ });
+ }
+ /**
+ * index
+ * @returns void
+ * @throws ApiError
+ */
+ public getApiLogin(): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'GET',
+ url: '/api/login/',
+ });
+ }
+ /**
+ * steam_authenticate
+ * @returns void
+ * @throws ApiError
+ */
+ public postApiLoginAuthenticate(): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'POST',
+ url: '/api/login/authenticate',
+ });
+ }
+ /**
+ * get
+ * @param windowStart
+ * @param teamId
+ * @param windowSizeDays
+ * @returns ViewScheduleResponse OK
+ * @throws ApiError
+ */
+ public getApiSchedule(
+ windowStart: string,
+ teamId: number,
+ windowSizeDays: number = 7,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'GET',
+ url: '/api/schedule/',
+ query: {
+ 'windowStart': windowStart,
+ 'teamId': teamId,
+ 'windowSizeDays': windowSizeDays,
+ },
+ errors: {
+ 422: `Unprocessable Entity`,
+ },
+ });
+ }
+ /**
+ * put
+ * @param requestBody
+ * @returns void
+ * @throws ApiError
+ */
+ public putApiSchedule(
+ requestBody?: PutScheduleForm,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'PUT',
+ url: '/api/schedule/',
+ body: requestBody,
+ mediaType: 'application/json',
+ });
+ }
+ /**
+ * view_available
+ * @param startTime
+ * @param teamId
+ * @returns void
+ * @throws ApiError
+ */
+ public getApiScheduleViewAvailable(
+ startTime: string,
+ teamId: number,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'GET',
+ url: '/api/schedule/view-available',
+ query: {
+ 'startTime': startTime,
+ 'teamId': teamId,
+ },
+ });
+ }
+ /**
+ * create_team
+ * @param requestBody
+ * @returns ViewTeamResponse OK
+ * @throws ApiError
+ */
+ public createTeam(
+ requestBody?: CreateTeamJson,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'POST',
+ url: '/api/team/',
+ body: requestBody,
+ mediaType: 'application/json',
+ errors: {
+ 403: `Forbidden`,
+ 422: `Unprocessable Entity`,
+ },
+ });
+ }
+ /**
+ * view_teams
+ * @returns ViewTeamsResponse OK
+ * @throws ApiError
+ */
+ public getTeams(): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'GET',
+ url: '/api/team/all/',
+ errors: {
+ 403: `Forbidden`,
+ 404: `Not Found`,
+ 422: `Unprocessable Entity`,
+ },
+ });
+ }
+ /**
+ * delete_team
+ * @param teamId
+ * @returns any OK
+ * @throws ApiError
+ */
+ public deleteTeam(
+ teamId: string,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'DELETE',
+ url: '/api/team/id/{team_id}/',
+ path: {
+ 'team_id': teamId,
+ },
+ errors: {
+ 403: `Forbidden`,
+ 404: `Not Found`,
+ 422: `Unprocessable Entity`,
+ },
+ });
+ }
+ /**
+ * view_team
+ * @param teamId
+ * @returns ViewTeamResponse OK
+ * @throws ApiError
+ */
+ public getTeam(
+ teamId: string,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'GET',
+ url: '/api/team/id/{team_id}/',
+ path: {
+ 'team_id': teamId,
+ },
+ errors: {
+ 403: `Forbidden`,
+ 404: `Not Found`,
+ 422: `Unprocessable Entity`,
+ },
+ });
+ }
+ /**
+ * add_player
+ * @param teamId
+ * @param playerId
+ * @param requestBody
+ * @returns any OK
+ * @throws ApiError
+ */
+ public createOrUpdatePlayer(
+ teamId: string,
+ playerId: string,
+ requestBody?: AddPlayerJson,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'PUT',
+ url: '/api/team/id/{team_id}/player/{player_id}/',
+ path: {
+ 'team_id': teamId,
+ 'player_id': playerId,
+ },
+ body: requestBody,
+ mediaType: 'application/json',
+ errors: {
+ 403: `Forbidden`,
+ 404: `Not Found`,
+ 422: `Unprocessable Entity`,
+ },
+ });
+ }
+ /**
+ * view_team_members
+ * @param teamId
+ * @returns ViewTeamMembersResponseList OK
+ * @throws ApiError
+ */
+ public getTeamMembers(
+ teamId: string,
+ ): CancelablePromise {
+ return this.httpRequest.request({
+ method: 'GET',
+ url: '/api/team/id/{team_id}/players',
+ path: {
+ 'team_id': teamId,
+ },
+ errors: {
+ 403: `Forbidden`,
+ 404: `Not Found`,
+ 422: `Unprocessable Entity`,
+ },
+ });
+ }
+}
diff --git a/availabili.tf/src/components/AvailabilityComboBox.vue b/availabili.tf/src/components/AvailabilityComboBox.vue
index dc5d5c5..611ed04 100644
--- a/availabili.tf/src/components/AvailabilityComboBox.vue
+++ b/availabili.tf/src/components/AvailabilityComboBox.vue
@@ -5,6 +5,7 @@ const model = defineModel();
const props = defineProps({
options: Array,
+ isDisabled: Boolean,
});
const isOpen = ref(false);
@@ -18,15 +19,15 @@ function selectOption(index) {
@@ -38,7 +39,7 @@ function selectOption(index) {
border-radius: 8px;
}
-.dropdown-container button {
+.dropdown-container .option {
display: flex;
flex-direction: row;
align-items: center;
@@ -53,7 +54,19 @@ function selectOption(index) {
cursor: pointer;
}
-.dropdown-container button:hover {
+.dropdown-container .option {
+ border-radius: 0;
+}
+
+.dropdown-container .option:first-child {
+ border-radius: 8px 8px 0 0;
+}
+
+.dropdown-container .option:last-child {
+ border-radius: 0 0 8px 8px;
+}
+
+.dropdown-container .option:hover {
background-color: var(--crust);
}
@@ -76,14 +89,14 @@ ul.dropdown > li {
list-style-type: none;
}
-.dropdown li > button {
+.dropdown li > .option {
padding: 8px 16px;
font-weight: 500;
font-size: 14px;
border-radius: 0;
}
-.dropdown li > button.is-selected {
+.dropdown li > .option.is-selected {
background-color: var(--accent-transparent);
color: var(--accent);
}
diff --git a/availabili.tf/src/components/AvailabilityGrid.vue b/availabili.tf/src/components/AvailabilityGrid.vue
index 895bd6e..d1d21f1 100644
--- a/availabili.tf/src/components/AvailabilityGrid.vue
+++ b/availabili.tf/src/components/AvailabilityGrid.vue
@@ -226,7 +226,7 @@ onUnmounted(() => {
}
.grid {
- display: flex;
+ display: inline-flex;
user-select: none;
}
diff --git a/availabili.tf/src/components/ComboBox.vue b/availabili.tf/src/components/ComboBox.vue
new file mode 100644
index 0000000..1b65da6
--- /dev/null
+++ b/availabili.tf/src/components/ComboBox.vue
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/availabili.tf/src/components/PlayerTeamCard.vue b/availabili.tf/src/components/PlayerTeamCard.vue
new file mode 100644
index 0000000..5ce519d
--- /dev/null
+++ b/availabili.tf/src/components/PlayerTeamCard.vue
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+ {{ player.username }}
+
+
+ |
+
+
+
+
+ |
+
+ {{ player.playtime.toFixed(1) }} hours
+ |
+
+ {{ new Date(player.created_at).toLocaleString() }}
+ |
+
+
+
+
+ |
+
+
+
+
diff --git a/availabili.tf/src/components/TeamsListSidebar.vue b/availabili.tf/src/components/TeamsListSidebar.vue
new file mode 100644
index 0000000..ff115ee
--- /dev/null
+++ b/availabili.tf/src/components/TeamsListSidebar.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
diff --git a/availabili.tf/src/main.ts b/availabili.tf/src/main.ts
index 5dcad83..ee6b674 100644
--- a/availabili.tf/src/main.ts
+++ b/availabili.tf/src/main.ts
@@ -1,14 +1,17 @@
-import './assets/main.css'
+import "./assets/main.css";
+import "vue-select/dist/vue-select.css";
-import { createApp } from 'vue'
-import { createPinia } from 'pinia'
+import { createApp } from "vue";
+import { createPinia } from "pinia";
+import VueSelect from "vue-select";
-import App from './App.vue'
-import router from './router'
+import App from "./App.vue";
+import router from "./router";
const app = createApp(App)
app.use(createPinia())
app.use(router)
+app.component("v-select", VueSelect);
-app.mount('#app')
+app.mount("#app")
diff --git a/availabili.tf/src/router/index.ts b/availabili.tf/src/router/index.ts
index 2c6e884..533b652 100644
--- a/availabili.tf/src/router/index.ts
+++ b/availabili.tf/src/router/index.ts
@@ -3,6 +3,8 @@ import HomeView from "../views/HomeView.vue";
import ScheduleView from "../views/ScheduleView.vue";
import RosterBuilderView from "../views/RosterBuilderView.vue";
import LoginView from "../views/LoginView.vue";
+import TeamRegistrationView from "../views/TeamRegistrationView.vue";
+import TeamDetailsView from "../views/TeamDetailsView.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@@ -27,6 +29,16 @@ const router = createRouter({
name: "roster-builder",
component: RosterBuilderView
},
+ {
+ path: "/team/register",
+ name: "team-registration",
+ component: TeamRegistrationView
+ },
+ {
+ path: "/team/id/:id",
+ name: "team-details",
+ component: TeamDetailsView
+ },
]
})
diff --git a/availabili.tf/src/stores/client.ts b/availabili.tf/src/stores/client.ts
new file mode 100644
index 0000000..b1f5b2f
--- /dev/null
+++ b/availabili.tf/src/stores/client.ts
@@ -0,0 +1,39 @@
+import { AvailabilitfClient } from "@/client";
+import { defineStore } from "pinia";
+
+export const useClientStore = defineStore("client", () => {
+ const client = new AvailabilitfClient({
+ //BASE: import.meta.env.VITE_API_BASE_URL
+ });
+
+ const calls = new Map>();
+
+ function call(
+ key: string,
+ apiCall: () => Promise,
+ thenOnce?: (result: T) => T
+ ): Promise {
+ console.log("Fetching call " + key);
+ if (!calls.has(key)) {
+ const promise = apiCall();
+ calls.set(key, promise);
+
+ // remove from calls once completed
+ promise.finally(() => calls.delete(key));
+
+ // only execute this "then" once if the call was just freshly made
+ if (thenOnce) {
+ promise.then(thenOnce);
+ }
+
+ return promise;
+ }
+ return calls.get(key) as Promise;
+ }
+
+ return {
+ client,
+ call,
+ calls,
+ }
+});
diff --git a/availabili.tf/src/stores/roster.ts b/availabili.tf/src/stores/roster.ts
index 25a4e28..81f2205 100644
--- a/availabili.tf/src/stores/roster.ts
+++ b/availabili.tf/src/stores/roster.ts
@@ -177,6 +177,13 @@ export const useRosterStore = defineStore("roster", () => {
"Roamer": "tf2-FlankSoldier",
"Demoman": "tf2-Demo",
"Medic": "tf2-Medic",
+
+ "Role.PocketScout": "tf2-PocketScout",
+ "Role.FlankScout": "tf2-FlankScout",
+ "Role.PocketSoldier": "tf2-PocketSoldier",
+ "Role.Roamer": "tf2-FlankSoldier",
+ "Role.Demoman": "tf2-Demo",
+ "Role.Medic": "tf2-Medic",
});
function selectPlayerForRole(player: PlayerTeamRole, role: string) {
diff --git a/availabili.tf/src/stores/schedule.ts b/availabili.tf/src/stores/schedule.ts
index 6a47d05..ec0ac95 100644
--- a/availabili.tf/src/stores/schedule.ts
+++ b/availabili.tf/src/stores/schedule.ts
@@ -2,56 +2,79 @@ import { computed } from "@vue/reactivity";
import { defineStore } from "pinia";
import { reactive, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
+import { useClientStore } from "./client";
export const useScheduleStore = defineStore("schedule", () => {
+ const client = useClientStore().client;
+
const dateStart = ref(new Date(2024, 9, 21, 0, 30));
const windowStart = computed(() => Math.floor(dateStart.value.getTime() / 1000));
const availability = reactive(new Array(168));
+ availability.fill(0);
+
const route = useRoute();
const router = useRouter();
const teamId = computed({
- get: () => route.query.teamId,
+ get: () => Number(route.query.teamId),
set: (value) => router.push({ query: { teamId: value } }),
});
watch(dateStart, () => {
- availability.fill(0);
+ fetchSchedule();
+ });
+
+ watch(teamId, () => {
fetchSchedule();
});
async function fetchSchedule() {
- return fetch(import.meta.env.VITE_API_BASE_URL + "/schedule?" + new URLSearchParams({
- window_start: windowStart.value.toString(),
- team_id: "1",
- }).toString(),{
- credentials: "include",
- })
- .then((response) => response.json())
+ return client.default.getApiSchedule(
+ Math.floor(dateStart.value.getTime() / 1000).toString(),
+ teamId.value,
+ )
.then((response) => {
- response.availability.forEach((value: number, i: number) => {
+ response.availability.forEach((value, i) => {
availability[i] = value;
});
return response;
});
+ //return fetch(import.meta.env.VITE_API_BASE_URL + "/schedule?" + new URLSearchParams({
+ // window_start: windowStart.value.toString(),
+ // team_id: teamId.toString(),
+ //}).toString(),{
+ // credentials: "include",
+ // })
+ // .then((response) => response.json())
+ // .then((response) => {
+ // response.availability.forEach((value: number, i: number) => {
+ // availability[i] = value;
+ // });
+ // return response;
+ // });
}
async function saveSchedule() {
- return fetch(import.meta.env.VITE_API_BASE_URL + "/schedule", {
- method: "PUT",
- credentials: "include",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- window_start: Math.floor(dateStart.value.getTime() / 1000),
- team_id: 1,
- availability: availability,
- })
+ return client.default.putApiSchedule({
+ windowStart: Math.floor(dateStart.value.getTime() / 1000).toString(),
+ teamId: teamId.value,
+ availability,
});
+ //return fetch(import.meta.env.VITE_API_BASE_URL + "/schedule", {
+ // method: "PUT",
+ // credentials: "include",
+ // headers: {
+ // "Content-Type": "application/json",
+ // },
+ // body: JSON.stringify({
+ // window_start: Math.floor(dateStart.value.getTime() / 1000),
+ // team_id: teamId.toString(),
+ // availability: availability,
+ // })
+ //});
}
return {
@@ -60,5 +83,6 @@ export const useScheduleStore = defineStore("schedule", () => {
availability,
fetchSchedule,
saveSchedule,
+ teamId,
};
});
diff --git a/availabili.tf/src/stores/teams.ts b/availabili.tf/src/stores/teams.ts
index a07a589..99b5ac0 100644
--- a/availabili.tf/src/stores/teams.ts
+++ b/availabili.tf/src/stores/teams.ts
@@ -1,36 +1,81 @@
import Cacheable from "@/cacheable";
+import { AvailabilitfClient, type TeamSpec, type ViewTeamMembersResponse, type ViewTeamResponse, type ViewTeamsResponse } from "@/client";
import { defineStore } from "pinia";
import { computed, reactive, ref, type Reactive, type Ref } from "vue";
+import { useClientStore } from "./client";
-interface Team {
- id: number,
- teamName: string,
-}
+export type TeamMap = { [id: number]: TeamSpec };
export const useTeamsStore = defineStore("teams", () => {
- //const teams: Reactive> =
- // reactive(new Cacheable([], 0));
- const teams: Ref<{ [id: number]: Team }> = ref({ });
+ const clientStore = useClientStore();
+ const client = clientStore.client;
+
+ const teams: Reactive<{ [id: number]: TeamSpec }> = reactive({ });
+ const teamMembers: Reactive<{ [id: number]: ViewTeamMembersResponse[] }> = reactive({ });
+
+ const isFetchingTeams = ref(false);
async function fetchTeams() {
- return new Promise((res, rej) => {
- fetch(import.meta.env.VITE_API_BASE_URL + "/team/view", {
- credentials: "include",
- })
- .then((response) => response.json())
- .then((response: Array) => {
- teams.value = response
- .reduce((acc, team: Team) => {
- return { ...acc, [team.id]: team }
- });
- res(teams.value);
- })
- .catch(() => rej());
+ return clientStore.call(
+ fetchTeams.name,
+ () => client.default.getTeams(),
+ (response) => {
+ response.teams.forEach((team) => {
+ teams[team.id] = team;
+ });
+ return response;
+ }
+ )
+ }
+
+ async function fetchTeam(id: number) {
+ return clientStore.call(
+ fetchTeam.name,
+ () => client.default.getTeam(id.toString()),
+ (response) => {
+ teams[response.team.id] = response.team;
+ return response;
+ }
+ );
+ }
+
+ async function fetchTeamMembers(id: number) {
+ return clientStore.call(
+ fetchTeam.name,
+ () => client.default.getTeamMembers(id.toString()),
+ (response) => {
+ response = response
+ .map((member): ViewTeamMembersResponse => {
+ // TODO: snake_case to camelCase
+ member.roles = member.roles.sort((a, b) => {
+ if (a.is_main == b.is_main) {
+ return 0;
+ }
+ return a.is_main ? -1 : 1;
+ });
+ return member;
+ });
+ console.log(response);
+ teamMembers[id] = response;
+ return response;
+ }
+ );
+ }
+
+ async function createTeam(teamName: string, tz: string, webhook?: string) {
+ return await client.default.createTeam({
+ teamName,
+ leagueTimezone: tz,
+ discordWebhookUrl: webhook,
});
}
return {
teams,
+ teamMembers,
fetchTeams,
- }
+ fetchTeam,
+ fetchTeamMembers,
+ createTeam,
+ };
});
diff --git a/availabili.tf/src/views/HomeView.vue b/availabili.tf/src/views/HomeView.vue
index 450999d..e97880a 100644
--- a/availabili.tf/src/views/HomeView.vue
+++ b/availabili.tf/src/views/HomeView.vue
@@ -1,12 +1,13 @@
+
JustGetAHouse
test
- Your Teams
diff --git a/availabili.tf/src/views/ScheduleView.vue b/availabili.tf/src/views/ScheduleView.vue
index 519b119..8d9e7a1 100644
--- a/availabili.tf/src/views/ScheduleView.vue
+++ b/availabili.tf/src/views/ScheduleView.vue
@@ -2,17 +2,17 @@
import AvailabilityGrid from "../components/AvailabilityGrid.vue";
import AvailabilityComboBox from "../components/AvailabilityComboBox.vue";
import WeekSelectionBox from "../components/WeekSelectionBox.vue";
-import { computed, onMounted, reactive, ref } from "vue";
+import { computed, onMounted, reactive, ref, watch } from "vue";
import { useTeamsStore } from "../stores/teams";
import { useScheduleStore } from "../stores/schedule";
+import { useRoute, useRouter } from "vue-router";
-const teams = useTeamsStore();
+const teamsStore = useTeamsStore();
const schedule = useScheduleStore();
+const router = useRouter();
+const route = useRoute();
-const options = ref([
- "TEAM PEPEJA forsenCD",
- "The Snus Brotherhood",
-]);
+const options = ref([ ]);
const firstHour = computed(() => shouldShowAllHours.value ? 0 : 14);
const lastHour = computed(() => shouldShowAllHours.value ? 23 : 22);
@@ -20,13 +20,20 @@ const shouldShowAllHours = ref(false);
const comboBoxIndex = ref(0);
-//const availability = reactive(new Array(168));
const availability = schedule.availability;
const selectionMode = ref(1);
const isEditing = ref(false);
+const selectedTeam = ref();
+
+watch(selectedTeam, (newTeam) => {
+ if (newTeam) {
+ schedule.teamId = newTeam.id;
+ }
+});
+
function saveSchedule() {
schedule.saveSchedule()
.then(() => {
@@ -35,14 +42,18 @@ function saveSchedule() {
}
onMounted(() => {
- teams.fetchTeams()
+ teamsStore.fetchTeams()
.then((teamsList) => {
- options.value = Object.values(teamsList);
- schedule.fetchSchedule()
- .then(() => {
-
- });
- })
+ options.value = Object.values(teamsList.teams);
+ // select team with id in query parameter if exists
+ const queryTeam = teamsList.teams.find(x => x.id == route.query.teamId);
+ console.log(queryTeam);
+ if (queryTeam) {
+ selectedTeam.value = queryTeam;
+ //schedule.teamId = queryTeam.id;
+ schedule.fetchSchedule(schedule.teamId);
+ }
+ });
});
@@ -52,7 +63,11 @@ onMounted(() => {
-
-
@@ -106,7 +123,10 @@ onMounted(() => {
diff --git a/availabili.tf/src/views/TeamDetailsView.vue b/availabili.tf/src/views/TeamDetailsView.vue
new file mode 100644
index 0000000..36ab69f
--- /dev/null
+++ b/availabili.tf/src/views/TeamDetailsView.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+ {{ team.team_name }}
+
+
+
+
+
+ Name
+ |
+
+ Roles
+ |
+
+ Playtime on team
+ |
+
+ Joined
+ |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/availabili.tf/src/views/TeamRegistrationView.vue b/availabili.tf/src/views/TeamRegistrationView.vue
new file mode 100644
index 0000000..5e05de3
--- /dev/null
+++ b/availabili.tf/src/views/TeamRegistrationView.vue
@@ -0,0 +1,143 @@
+
+
+
+
+
+
Create a new team
+
+ Register your team to streamline match scheduling, role assignments,
+ and overall team communication.
+
+
+
Team Name
+
+
+
+
+
+ Announcements Webhook URL
+ (optional)
+
+
+
+
+
+
+
+
+
diff --git a/availabili.tf/vite.config.ts b/availabili.tf/vite.config.ts
index 9610216..31bbd5c 100644
--- a/availabili.tf/vite.config.ts
+++ b/availabili.tf/vite.config.ts
@@ -16,7 +16,8 @@ export default defineConfig({
server: {
proxy: {
'/api': {
- target: 'http://localhost:5000',
+ //target: 'http://localhost:5000',
+ target: 'https://on-indirectly-firefly.ngrok-free.app',
changeOrigin: true,
secure: false,
configure: (proxy) => {