6276 lines
378 KiB
JSON
6276 lines
378 KiB
JSON
{
|
||
"definitions": {
|
||
"Assignment": "2b8f7430f4db7252b25e9db231c6684f87ca2de21e2933d107c9773888ea661f",
|
||
"Base": "c1cbd280eee0b89102476fb3994dacd59844e10d24e86fda68c649fd3c1b1c59",
|
||
"MonthBlock": "287cce589bc05e5da096fabe126ddfffbf693c4ce32829aa97f9f3e6d6c0df84",
|
||
"MonthlyPattern": "00e73673967d1aa86d664c0c0b89ff55156638c63e544a16228574a0c4ef9c78",
|
||
"NotificationLog": "8b7291e357576d5ca0ca6d5bafe340bdbdfc5bcf541fcb10097be5a0367e4507",
|
||
"RotationMember": "43203a53701c7433992f94be9288c2dfaecd4ca6ab58b6b2577330068d61a3ac",
|
||
"Schedule": "50a3afabda23f150960b9a5372eeac29b49646243cffc0a1c24fb4e691b806e9",
|
||
"Settings": "9ec5987f4c5d99c6cc65597be1d95b4aa8d2bbb7ee8d6445690dfe41184f4957",
|
||
"SwapRequest": "a699dcbc2bfcc415d993ead48c028663ab06e8eb54bc77516235f174a51589c5",
|
||
"Team": "989de12ee34d86bd747d257441e2a2fb3718e0053790ed95c146df5cc1ab0924",
|
||
"User": "941134ed5efbd2464e8e8918a0138ee2bdaa228fa5e1f988ef0e3436075334d6",
|
||
"_accepting_team": "d65db64047442d79f4654985e0cf1aa15614570e7586eedda1f81376ca75a3db",
|
||
"_active_request": "df41ac832e5720158fbbb8b176979b3e50697dcdcef6ea123a7b7d28df153cc0",
|
||
"_active_schedules": "d0fca80985d60f88ccc47c8b4ea603761812c35fc3bcc8f80253c387c0775eea",
|
||
"_admin_teams": "841c05fa8ed4eef1426509763d55e950fd665f9f477fd97c95683b9439707699",
|
||
"_all_schedules": "8fe6504cecab40a403132f13031277d61e276b337b5fee5b1fe686831dde9bf8",
|
||
"_apply_pattern_forward": "50ad2b7df7843ec36efc3c61d4a7252d63d4121c3944d5e735f2160a421676f5",
|
||
"_apply_pattern_to_open_rows": "24076e69176477dd74da34c7f69a1342bef292e5ad0f42732674edda5b98f0d3",
|
||
"_apply_rotation_to_rows": "a2a02c003b9b9e4fc64023898bcc98c5aa5173be6fa8b86f8c023e2a9b5e35c6",
|
||
"_assign_day_url": "351850c9e71cc95cc25579bf3a7e145d284c5883ab749b8da49cbd21aa59cff1",
|
||
"_assign_user_to_teams": "cf2c8ab64e7bdccf149f7545c09a889ccd9d0383aa8a79fc3d0412dbba62edf7",
|
||
"_calendar_token": "d33a7dbd8af12b55164b2c544c8f3a4c7527b359136d8937592a9d79578c0076",
|
||
"_check_csrf": "96b5b121cc98f9043f0fa41b4bb19b3bf700ad892702c24ea8b3bce1f0782121",
|
||
"_chosen_user": "baa8d0dcc33d5cde5b0c9f236ea483508d11763f462033680703894d845d2f44",
|
||
"_clear_assignments_from": "573dbe74cb5555869db83a92968b5280d37cf9f0e5d72194b81d7a866c3e9403",
|
||
"_columns": "9e9ac42b8d5c8d5f3f6b81d9222df02b18f1c9670e3d064f1b7ef30f0af2fb3a",
|
||
"_complete_swap": "507ee15bb500ce0f40f77e90b5f6c02c4e94b1d4c95ce5366a76994f923eca59",
|
||
"_context": "baa2d501f6e418152cbebc56f07dfe55eb35154829416a15ff7fa79ba2a3e467",
|
||
"_create_swap": "64cb526a882e09a01945b960f87de7724cae7322206a0a5a65cafa472c352b17",
|
||
"_csrf": "4be30b4c04bd4246f08fed3782ff0155eea4eb691032b6c0cd6e98211e9a7a7b",
|
||
"_current_user": "42bb53fff71dff01cf1455a85817a8975178bfb9faab6221e11eef56deb7df11",
|
||
"_default_team": "6730fd6dbed43670b09335d507eef9e3ebfd0df7038912cfa28e7d926b97abb3",
|
||
"_escape": "0e70e07ca6156c79fed26b865c204f7022e83e5641a32b2d9a30b0ef76dca618",
|
||
"_event": "597dd397cd3369ce32e3b439b4d3143c09cbe97cd3ebebead88424087fec5b90",
|
||
"_flash": "f319e7a8b69c1b1654887f3b7e7ac724a8286e9d6f8c8def05e49be3b1936b42",
|
||
"_fold": "2082572e5cf97c4d236edd0f59b0729b4a169fceed9dc63b61b9bbf356e71ac7",
|
||
"_generate_rotation_range": "5a3c32c3e8a0066436fb34f55a8345a0ad36633462bfc2c8c00af07f99938068",
|
||
"_get_schedule": "c658002d06bcba95b148651477f09a125aaa6982f6e260d9f0d67aac04f64728",
|
||
"_host": "0587b3d3b29c9065e9d64bcf7559e80e2695dbc89929ae0cb28820297de8f9bf",
|
||
"_layout": "4c670e9cce524c679deabb4cbd1a17f89e431c2f059442a6c20e75a1573d73e9",
|
||
"_loses_last_team": "d23d67a6a08920b6eb31f7671f90eb0c1c2078f139182a8e17452ca035cda286",
|
||
"_month_assignments": "922fa4b0c0df6a77641168d2d2dbab5437977524c12958d5bc89f7289e4119d5",
|
||
"_month_nav": "0a5a7f4fe43eefab3b1b4a86e71f2b87251371d571786cec7729f40976d0db6b",
|
||
"_new_user": "6998933630f0044d5a9254ce1dc9ae8ac11dcb010c8ada965158ff114d82acf5",
|
||
"_next_dates": "f103e97d6350e7db09aa014c4cbcd4fcfcaadcf6b7c8dfe604eed07447a67740",
|
||
"_notify_assignment_changes": "29c60bf23d25aa77aaf770388f0a6fb8558216c8737182970b7331fb12ce8e33",
|
||
"_open_swap_requests": "f095afa9428aed7f1af9d524fd05b118a99518b919a9880089c1a9cf6b9b0e9b",
|
||
"_parse_month": "f364954e7acf36646e60bba89492d98d9a0b9474c1321c862db4afd369a44ed5",
|
||
"_parse_time": "3a2c7134356e971c5f5c30f60df74ea6f2de74ae7eb097efcc49e2b599c33dc0",
|
||
"_pattern_map": "8b94d9b9c5c57adfe91ae5287abfafaa260b888dae77b3d76f3e6d3d98360d6a",
|
||
"_redirect": "f78bec5bb9fabde6e3b768a3a6d4383785db3d42a8e355517f52f8d00af1240a",
|
||
"_reject_swap": "e9c09521a9b3dca0950bf61ee08dc5edefa9112619d37ecd0b8df1db5e18cd57",
|
||
"_rejectable": "6c86715704c166a334736e0cb79c5df74cddb39d42a91ee84b82d5e6b1ebaddd",
|
||
"_render_admin_schedules": "75c2260c9410ef8dec1fe023e7bca6ef6c719d2357eb90a9e05065d672f85bbc",
|
||
"_require_admin": "5b64c94b267b2e073e5346314b697e0159719f926f86d6baf9ba25c852b14098",
|
||
"_require_removable": "789c50862c3c058de4f88cf97e84a34de77d5bd846ad5982761490e58988e6c5",
|
||
"_require_user": "9b602a8921681453dd541d63af2fb5c1e59a0571398ae867e6f11037341dddcd",
|
||
"_rotation_occurrence_index": "e0a5e24763920abae350ffb3090f7f10b0d1034fa1ee618c646e6a9f9c2bcd4c",
|
||
"_safe_back": "99eae35a7eaea120c822dfa8a6c73dd3628a177ea896b34adccbbaca94e92fa3",
|
||
"_save_assignments": "6fbc815ff286c0500cec0860939aedea5876d5acffa4255f3c870d8740371685",
|
||
"_schedule_card": "e9f09d2c6a0695b219019b9ecdd4bebb3df9b4596020a5a8cd7e9b410392644d",
|
||
"_schedule_groups": "bdc5ede2e7cde69e09a1ac679b977cbf94414e0c56be16c92ad196e00de15ba5",
|
||
"_schedule_page": "3c6cf42c5c0182d017ddff4c48e54c32292a6074cded9a8e923179eaf9bf1d26",
|
||
"_schedule_team_ids": "9c8759326172090b172c581384ce50c0ec12a2f2e3f252bbd24d587d66ed193f",
|
||
"_schedule_teams": "0eb69900958f5cd1e2d2790a22f66b499585c5e1b1a6e9f37e1becd26a6005c8",
|
||
"_schedule_teams_map": "9b6434331fb2a6fc0603ddf2c4c049fc506710a23609890af8f2c9df384c0726",
|
||
"_set_assignee": "6661e42e0b623187b9a94f1967bf02e50471ec1f6ff4f9a402170dfdc1dada4f",
|
||
"_shared_calendar_url": "8cdc4d40e4286372570ee2ecd0d263ae31a6252700d3815ba1665f714c0885a2",
|
||
"_stamp": "6b94fc759e61245dc15fe0ad39892899cc4f4222317f36fd5f394d35424c1e26",
|
||
"_swap_ownership_holds": "3f73630e67f92ab23f7c6db4d02c4e83240b25b645fcf3f8ad26b0506249b9c6",
|
||
"_swap_partner": "0faa38b24ae828587bab0da613b14f64046a743f347d65e07e45bb3d73b04ec4",
|
||
"_swap_side": "08c5a56b48cf3a65e526b84c07efc890e902270d2945014cefc4fcf11c870efe",
|
||
"_team_removal_blocker": "54371319c371902d06dff4f21fc22c95470af2b44f222fb9c07a1e26a722c351",
|
||
"_team_removal_impact": "2881010e02c6b9cc55103d2f1a104222f113652f67c864e97a8dd2fbfcbff9a5",
|
||
"_timezone_block": "b653b1b2af407bca96cea3a5399566c57d0645239f9ae89a2307aa5300119abd",
|
||
"_usable_admin_users": "699592d85a158c406d747f042f58d35668cfa70f71800ed8ab78066c371f92ee",
|
||
"_user_label": "83c65b6ef7163866c30c4d0178143dc3723e00db7bfed515f73515878469d856",
|
||
"_user_removal_blocker": "66d9034be0907e2d142faa2b4cbc116e2f1a366fbaa44881ecd1a4f3781374b0",
|
||
"_user_team_ids": "d643445a1676ea45ae29f3c4272e60f297fc7cab934eb904fc30d93174a4e10f",
|
||
"_user_teams": "cf4a74bc2fe491f76dfd99ad943398b90cb0490b3853d9a9759303759159d72b",
|
||
"_users_by_id": "027ccd114b6404a77b73d99276a18758ff92a44f68ed7a0b5e4afa4fcfe97aca",
|
||
"accept_swap": "84d54fe1b19966de00e31d7db4ea85910053b6887ae1a31c89f4d3ccb125cb71",
|
||
"account_page": "eed96aa30d46aa92c42356a961cef63e3847e2ecc5d05740510dd3d413018a6c",
|
||
"account_password": "4545d86d3e2a1a2d2dbd266167b873900ffb82d981fc5d5d36d58faa5dc0ed6e",
|
||
"admin_apply_pattern": "a71528fe2456fe20201e97df6b936ca0a7d7edc59a5950b30ca8db57168d5d62",
|
||
"admin_apply_rotation": "d5c45d851ba1929c2fa637101f13bcb972c59a2c213a0a51f7e12721599b5275",
|
||
"admin_assign_day": "f14c110ae7654bcdcda09f5a2462bbd96fced63f216ff4b6b13d97370ed7d3b6",
|
||
"admin_assign_team": "9b76661f576260bb5325c242dd21c6c543756327b4d8416c2f8c0471adb44b88",
|
||
"admin_assignment_regenerate": "41082e4adcf257240ef5520268f8c611f2836fdddd6e5908564331c894a639bd",
|
||
"admin_clear_from": "ed1cc3f147a1318c8594edbce9785b13206ca5dbd8c8c3d560e07418dc2cf646",
|
||
"admin_create_team": "b33dd7f9dac0a1aa9557d20b4eee6ffee38ab2cb49ed2e7f12d63ecb6ebc8d51",
|
||
"admin_create_user": "2b08d3f2cb2ece36612585d1096b0c81e2b35d8bb39a1985cfaac8c03f2b14bb",
|
||
"admin_month": "eb75e48f5f33f84ade39b9f6a117d3b43c7fd340938d0618c33c418d1ae986d0",
|
||
"admin_notifications": "c9c76ee7f89a0c0d233dd29390bdeacf0b4b57bfae4a996c6aa0044f923ce886",
|
||
"admin_reject_swap": "ee3da8617187b8594a9f4e8d700c2dd2fe3fb71c7f4e64a3151acacd1838c1c2",
|
||
"admin_rotation_add": "6d688b41898fc4be7a6823b65fe51964e97971dcf9381ee5104d02f0a79b48a8",
|
||
"admin_rotation_move": "c140ad14df549d198b96e4f667b419eb9c4679d863ef4f8f5f8319310dff2924",
|
||
"admin_rotation_remove": "83ee03662a0e1e4eb0138e8312f347c694e066dff2959953f9395b4ffb53586d",
|
||
"admin_schedule": "8749d7d1e1964e73d91da3594e333d75bf7aa1e65beb44b001e4e3331c8b5e63",
|
||
"admin_schedule_assign_swap": "36c2cb1e871bcecd42fab64807d967f5e7c906fbb93763eda086b7a636315f15",
|
||
"admin_schedule_assignments": "f7d833d769a4a7967da81c25267080be799414b1d26459dbae8bcf5e37cb970a",
|
||
"admin_schedule_pattern": "fc11cb71db195140b36f33a6115ef63f0f9251c3b8b581c5988e1d4955c3a41e",
|
||
"admin_schedule_settings": "aa33b00fcebeebe288ea69c480f254c0e4fd92fe60cd2f0a366370df26cac576",
|
||
"admin_schedules": "456005bcb0cce35f16dc22d77cf9cab459814697da0fa1b71ed80b06abe6d571",
|
||
"admin_team_delete": "8202aeaaa7c478a874f6d5431b8e08c2c876b1adcb27c90c1b805d57de09f974",
|
||
"admin_team_delete_page": "2eb324bf154ddcb0efbd732637a9aa16da7694e0d2354bc9869463d0b2bd9fa2",
|
||
"admin_teams": "8a768f6b5e693318d37ea96e6f2f9ae05cbf0c87a785908ba79bf1338feda490",
|
||
"admin_update_team": "a214c0ce72227a0b2865a0a8a9dad73e6703ac4bc8f675903b01a4d0d436faab",
|
||
"admin_update_user": "320453b7a8a37ce03d01763b3192037df8b8149da4a9b4ab911d8a4a1dd8aea2",
|
||
"admin_user_delete": "b35988c6b835b8a88d67ec87ae9fe76ba80badc4057a975fd383d6dae7573cb0",
|
||
"admin_users": "ab6dfe526dce24d96c369e0602ac8207ef5af570d9b2765ce8b3fcb6b285e1e4",
|
||
"alembic_config": "2952ea6c5985853514aae3e8a7b74f0f1d0d49b56b45ca0c82874d7673ae574f",
|
||
"approve_swap": "fe63883c1247cdec87036e1093c8675a8dbbdf55a98cf891de8fd73fca23fe03",
|
||
"assignments_by_date": "3cf3707a6fd0431806e068c7513781e3f36b77a6c38bc261ba8c5817d9ba446f",
|
||
"backup_database": "ece880319f6cec2023f52c7c6cc6f1ba56df575b9263abb8773efa2f152c7f46",
|
||
"build_feed": "c161e5ec4587a8c188242cd891be89cec2cd760faf6f93f8b2a3bf815ae7c3c9",
|
||
"build_schedule_pdf": "99ec4859f0f8332e38a4ef974b1a24d873f8f227320a9c151924cf7f616edc10",
|
||
"calendar_feed": "a9092ad9e9c3a713f8b12425e708c183723ac1704f57b1828583b11c77ad6f03",
|
||
"calendar_page": "5af6e6706128abfda036e1f40f5e0cc2196870f58a12b558aad0e298ff0e8da0",
|
||
"cancel_swap": "f70e2ad1ea81e6e3a3de61341d2141a31b9ec2a5d7aa92060e1504b8f548b122",
|
||
"create_swap": "8d4046a547b2c977fe0d7a9e1d6befd032a4fefbf8a7a947cf1cd4f0bf46af4d",
|
||
"create_swap_for_chosen_shift": "e1d61736a3abd5f6781db7ff27dcc10dd150e608e3118f22b9108acbb1155a10",
|
||
"current_revision": "5cfbec0632f55ac47e35defd403e4fc376c1dfe41d3d7e2257cc979ce30df1e6",
|
||
"dashboard": "2fe7dc610a557407ba095415126ecd02410041b061188fe680608c6798194888",
|
||
"database_file": "3623fea87f7c0a7c7341c83acaab3ae265ec915f85e128e3cae61529c6a439bc",
|
||
"day_long": "6b5c7e11721516feb42c0e6c826ec8a86e0d523face81638c6012a3bb8217c1a",
|
||
"day_numeric": "70aff7071c0ecc5467a92e65a3a03b0c0a22c8dade6f38d8ef258369c95fca6d",
|
||
"day_numeric_long": "43e31e8fb96375cd431d46d02d624ab267890be75ed72d0fe3a10a0f5b5d2aa4",
|
||
"day_short": "4e070187de377679c1a84343eb19379c6e11612aff9dcd9af6962dc376e4d9a3",
|
||
"db_shell": "a035d7c3181bfba100122efe7d1b7c5db7560442179e82d30dc8dba3c9d101fe",
|
||
"download_schedule_pdf": "6664e4a367234ccaaef21ac51c505bd7269df98672cb3e272a0f6c7de0f00d58",
|
||
"ensure_initial_data": "50e3dccd6cf986881214f712f207a8e3286e438d87b310eec3b47013865ca625",
|
||
"ensure_month_assignments": "24a5fed180353adee1205d0f5fbd2eb82faa71f79d8a0c9884dba4ebe1010ab8",
|
||
"event_type_label": "1cbd11dd0645812c165723f4ac12330e5b3a79e21d55b894951ae091693dcca3",
|
||
"export_schedule": "55cf4b4cedf517ca639948c814bc073822bbf7cfd270c4b0f71b112a04ebbe5f",
|
||
"feed_timezone": "2cc659e76a7f6f83f57ff4b0d171edfbbdbc62a1a0d798185525836b42f6ecb1",
|
||
"get_db": "c77eb1c621611e31ab28319b17065da64d30533baa04c2a5aeba743a293bbb0c",
|
||
"has_tables": "9f5ad997bff49fb9276e0cf612de0e9a88eca2fccdac8692b088f71ad2b30129",
|
||
"hash_password": "40ddad2e156cf6dfc9fb4ee36f35e0d08ad8d541989b209fcab568d4204cc0d9",
|
||
"head_revision": "7d5edc10e7ee8f86a7dd77c28803c61b1adf88a649d74987f924bef8b1e0e073",
|
||
"health": "53803ecc82d556044c97eaf1c54171a0f2cccb6bb9b4d0ec0dea11d30e2f8623",
|
||
"init_db": "4d6e2c0fc4f010daa7cd8df474365f003b816d52345294d42e09fab0314f79c2",
|
||
"iter_month_dates": "5bdf4897859c15f5a3c3a66c80316d261831dbb4ae2faa36872202feec22d383",
|
||
"lifespan": "81b73c45932fcba7199a0d47ff4536f9961c293a7396f2cf82a911b48ac93b1e",
|
||
"login": "f0fa25a608d15dfaeec681697ddc9f5d49bfe302f7a5a017c4cee68d99c90db3",
|
||
"login_page": "465ec7327c51717a94fa4020bc3e4ca5d8af5d2f30352e87434f212ae999f054",
|
||
"logout": "60430dd402ba181a2aa2da2f8716ea3f4581a7d68ffe9156724be0446b213ec5",
|
||
"main": "f678f9d42b7760d3adb188e28bd32e1f437dcf373edd8433ba585c602deba288",
|
||
"migrate": "0f75de0991513bd44872cbd512b4fb34e266aa5894833f6efde1ae3a37acc516",
|
||
"moment": "236fb581bc10ba7d82702f3205fdd76475b7a0d2355229017dd2216bacb8a525",
|
||
"month_bounds": "0b1a85e2ad4419670077ed19ef9305e2b2ba61f43c86db27b931d1c7c21f06fe",
|
||
"month_name": "25928f05d2b10db526e15d96c442707d3d370e470cdc40a7a76f22905594d06b",
|
||
"new_csrf_token": "59bd529fd8605261ff2dac1348bd808cd1bfab8a062a68a0218e1fc8b65abd64",
|
||
"new_swap_page": "62fb0393a48703ad89379e6c1e12fe30797a3970366f97cb209ccaadd741b45d",
|
||
"notification_status_label": "1b63a299a84917b66e61ea76d42bc806e13fc147df5c25466f09efafaa4aeabe",
|
||
"pattern_team_for_date": "febe4323146b290652d2995078eda9f8b5761ea99fc267bd4ca9ffab9a78aecf",
|
||
"pattern_teams": "a1bbd5380c67eb9e142ec4a105fd3bdf0243e8d7642fcc24d0eb7b024ee8347c",
|
||
"reject_swap": "fc96bdd9d2ac28f6c46fd9a314c918f17c44d963beda0dd15a8511490091aa83",
|
||
"revert_swap": "c033fc0d5368da573ca33e41df6463c029fd96d61e2e36775149a98e221b79bf",
|
||
"rotation_team_for_date": "e6015fbc5d2bc3e152a02e22b3ea8f2cbb9075c2817f5e4f711048d16b180eb4",
|
||
"run_migrations": "9db018c5ab941a1fd5fee9b233b92ad649e2416cbf8ae605c8fc4b4b35712c9e",
|
||
"seed_demo": "d6141cc2710340cfc9ca3282b2d4a8018461d7134f4ab351a9d9dd653aeb40df",
|
||
"send_email_notification": "438763f138dc3103beaf1c3eac3e8a5501b57c8b1f520888c8da27e24c087c0a",
|
||
"send_many": "43af810d2e2a4f066c1785f6d0ae6738bec3063d95146c95076b84c9f7d13a4f",
|
||
"send_reminders": "351f084c07dcb6dc4433b43eb4493b195bfaaab7d8627774bd3ca0776e6a4912",
|
||
"source_label": "2b6551908dc0c237830b10aa17edee9822150f6c67cac4e9d53f2fbf62a8fc35",
|
||
"stamp": "953ddcd55bb2f00e293f821a5583565ef793704dffcfc85e0b7a5a95241acae1",
|
||
"swap_status_label": "bf46d5b8e6e685882063adbd33b363f39479e5f7e24ce2aeb970924d4fb5216b",
|
||
"swaps_page": "bb2bc978469a6e93f909d6a3c20af4aeaf5397c55013bc7edd85e7714ec9c020",
|
||
"verify_password": "bcc868c5552a7003a2fa8798c81403aa21d7fe5c35c7ecc81d9c29effaf8f974",
|
||
"weekday_name": "bad38b61f1dd398814f0033823c70ea9b0d57a638a0bf78fb9ff806c05da9e92",
|
||
"weekday_names": "a9d41b91b2d5b7b057f9b3325cf970df90a992348af215425119dfd7854096ac",
|
||
"window_label": "52b13470c59d8246d8ef3d28ffa0adae728eceef25e77320c388a26a28ef7fa0"
|
||
},
|
||
"migrations": {
|
||
"README": "31595cf53626af9ed16e15c44fa43183209cc163fbc3ebcb904b22ac436a8884",
|
||
"env.py": "2094c197e04306806e783814065c4a47f98dd7764315ffac6e676130c0ee202e",
|
||
"script.py.mako": "d38920781b4d31ae1c0a71bc09f41c2ade8feab9e5a087e3f30cb4bbf0c1c9d3",
|
||
"versions/0001_initial.py": "6b5f1c37860c1cde017565a39464bf9b2635840c695f1f06a874845e3fa3ff6a"
|
||
},
|
||
"openapi": {
|
||
"components": {
|
||
"schemas": {
|
||
"Body_accept_swap_swaps__swap_id__accept_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_accept_swap_swaps__swap_id__accept_post",
|
||
"type": "object"
|
||
},
|
||
"Body_account_password_account_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"new_password": {
|
||
"default": "",
|
||
"title": "New Password",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_account_password_account_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_apply_pattern_admin_schedules__schedule_id__apply_pattern_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"month": {
|
||
"title": "Month",
|
||
"type": "integer"
|
||
},
|
||
"year": {
|
||
"title": "Year",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
"required": [
|
||
"year",
|
||
"month",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_apply_pattern_admin_schedules__schedule_id__apply_pattern_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_apply_rotation_admin_schedules__schedule_id__apply_rotation_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"month": {
|
||
"title": "Month",
|
||
"type": "integer"
|
||
},
|
||
"year": {
|
||
"title": "Year",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
"required": [
|
||
"year",
|
||
"month",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_apply_rotation_admin_schedules__schedule_id__apply_rotation_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_assign_team_admin_assign_post": {
|
||
"properties": {
|
||
"assignment_id": {
|
||
"title": "Assignment Id",
|
||
"type": "integer"
|
||
},
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"team_id": {
|
||
"default": "",
|
||
"title": "Team Id",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"assignment_id",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_assign_team_admin_assign_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_assignment_regenerate_admin_schedules__schedule_id__assignments__assignment_id__regenerate_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_assignment_regenerate_admin_schedules__schedule_id__assignments__assignment_id__regenerate_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_assignment_regenerate_admin_schedules__schedule_id__assignments__assignment_id__use_rotation_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_assignment_regenerate_admin_schedules__schedule_id__assignments__assignment_id__use_rotation_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_clear_from_admin_schedules__schedule_id__clear_from_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"month": {
|
||
"title": "Month",
|
||
"type": "integer"
|
||
},
|
||
"year": {
|
||
"title": "Year",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
"required": [
|
||
"year",
|
||
"month",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_clear_from_admin_schedules__schedule_id__clear_from_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_create_team_admin_teams_post": {
|
||
"properties": {
|
||
"assigned_user_id": {
|
||
"default": "",
|
||
"title": "Assigned User Id",
|
||
"type": "string"
|
||
},
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"email": {
|
||
"default": "",
|
||
"title": "Email",
|
||
"type": "string"
|
||
},
|
||
"name": {
|
||
"title": "Name",
|
||
"type": "string"
|
||
},
|
||
"notify_email": {
|
||
"default": "",
|
||
"title": "Notify Email",
|
||
"type": "string"
|
||
},
|
||
"phone": {
|
||
"default": "",
|
||
"title": "Phone",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"name",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_create_team_admin_teams_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_create_user_admin_users_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"is_admin": {
|
||
"default": "",
|
||
"title": "Is Admin",
|
||
"type": "string"
|
||
},
|
||
"name": {
|
||
"title": "Name",
|
||
"type": "string"
|
||
},
|
||
"password": {
|
||
"title": "Password",
|
||
"type": "string"
|
||
},
|
||
"team_ids": {
|
||
"default": [],
|
||
"items": {
|
||
"type": "string"
|
||
},
|
||
"title": "Team Ids",
|
||
"type": "array"
|
||
},
|
||
"username": {
|
||
"title": "Username",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"name",
|
||
"username",
|
||
"password",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_create_user_admin_users_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_reject_swap_swaps__swap_id__admin_reject_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_reject_swap_swaps__swap_id__admin_reject_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_rotation_add_admin_schedules__schedule_id__rotation_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"team_id": {
|
||
"title": "Team Id",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
"required": [
|
||
"team_id",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_rotation_add_admin_schedules__schedule_id__rotation_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_rotation_move_admin_rotation__member_id__move_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"direction": {
|
||
"title": "Direction",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"direction",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_rotation_move_admin_rotation__member_id__move_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_rotation_remove_admin_rotation__member_id__remove_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_rotation_remove_admin_rotation__member_id__remove_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_schedule_assign_swap_admin_schedules__schedule_id__swaps__swap_id__assign_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"month": {
|
||
"default": "",
|
||
"title": "Month",
|
||
"type": "string"
|
||
},
|
||
"team_id": {
|
||
"title": "Team Id",
|
||
"type": "integer"
|
||
},
|
||
"year": {
|
||
"default": "",
|
||
"title": "Year",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"team_id",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_schedule_assign_swap_admin_schedules__schedule_id__swaps__swap_id__assign_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_schedule_settings_admin_schedules__schedule_id__settings_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"end_time": {
|
||
"default": "",
|
||
"title": "End Time",
|
||
"type": "string"
|
||
},
|
||
"is_active": {
|
||
"default": "",
|
||
"title": "Is Active",
|
||
"type": "string"
|
||
},
|
||
"requires_manager_approval": {
|
||
"default": "",
|
||
"title": "Requires Manager Approval",
|
||
"type": "string"
|
||
},
|
||
"start_time": {
|
||
"default": "",
|
||
"title": "Start Time",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_schedule_settings_admin_schedules__schedule_id__settings_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_team_delete_admin_teams__team_id__delete_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_team_delete_admin_teams__team_id__delete_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_update_team_admin_teams__team_id__update_post": {
|
||
"properties": {
|
||
"assigned_user_id": {
|
||
"default": "",
|
||
"title": "Assigned User Id",
|
||
"type": "string"
|
||
},
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"email": {
|
||
"default": "",
|
||
"title": "Email",
|
||
"type": "string"
|
||
},
|
||
"is_active": {
|
||
"default": "",
|
||
"title": "Is Active",
|
||
"type": "string"
|
||
},
|
||
"name": {
|
||
"title": "Name",
|
||
"type": "string"
|
||
},
|
||
"notify_email": {
|
||
"default": "",
|
||
"title": "Notify Email",
|
||
"type": "string"
|
||
},
|
||
"phone": {
|
||
"default": "",
|
||
"title": "Phone",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"name",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_update_team_admin_teams__team_id__update_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_update_user_admin_users__user_id__update_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"is_active": {
|
||
"default": "",
|
||
"title": "Is Active",
|
||
"type": "string"
|
||
},
|
||
"is_admin": {
|
||
"default": "",
|
||
"title": "Is Admin",
|
||
"type": "string"
|
||
},
|
||
"name": {
|
||
"title": "Name",
|
||
"type": "string"
|
||
},
|
||
"password": {
|
||
"default": "",
|
||
"title": "Password",
|
||
"type": "string"
|
||
},
|
||
"team_ids": {
|
||
"default": [],
|
||
"items": {
|
||
"type": "string"
|
||
},
|
||
"title": "Team Ids",
|
||
"type": "array"
|
||
},
|
||
"username": {
|
||
"title": "Username",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"name",
|
||
"username",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_update_user_admin_users__user_id__update_post",
|
||
"type": "object"
|
||
},
|
||
"Body_admin_user_delete_admin_users__user_id__delete_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_admin_user_delete_admin_users__user_id__delete_post",
|
||
"type": "object"
|
||
},
|
||
"Body_approve_swap_swaps__swap_id__approve_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"team_id": {
|
||
"default": "",
|
||
"title": "Team Id",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_approve_swap_swaps__swap_id__approve_post",
|
||
"type": "object"
|
||
},
|
||
"Body_cancel_swap_swaps__swap_id__cancel_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_cancel_swap_swaps__swap_id__cancel_post",
|
||
"type": "object"
|
||
},
|
||
"Body_create_swap_assignments__assignment_id__swap_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"message": {
|
||
"default": "",
|
||
"title": "Message",
|
||
"type": "string"
|
||
},
|
||
"target_assignment_id": {
|
||
"title": "Target Assignment Id",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
"required": [
|
||
"target_assignment_id",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_create_swap_assignments__assignment_id__swap_post",
|
||
"type": "object"
|
||
},
|
||
"Body_create_swap_for_chosen_shift_swaps_new_post": {
|
||
"properties": {
|
||
"assignment_id": {
|
||
"title": "Assignment Id",
|
||
"type": "integer"
|
||
},
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"message": {
|
||
"default": "",
|
||
"title": "Message",
|
||
"type": "string"
|
||
},
|
||
"target_assignment_id": {
|
||
"title": "Target Assignment Id",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
"required": [
|
||
"assignment_id",
|
||
"target_assignment_id",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_create_swap_for_chosen_shift_swaps_new_post",
|
||
"type": "object"
|
||
},
|
||
"Body_login_login_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
},
|
||
"password": {
|
||
"title": "Password",
|
||
"type": "string"
|
||
},
|
||
"username": {
|
||
"title": "Username",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"username",
|
||
"password",
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_login_login_post",
|
||
"type": "object"
|
||
},
|
||
"Body_logout_logout_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_logout_logout_post",
|
||
"type": "object"
|
||
},
|
||
"Body_reject_swap_swaps__swap_id__reject_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_reject_swap_swaps__swap_id__reject_post",
|
||
"type": "object"
|
||
},
|
||
"Body_revert_swap_swaps__swap_id__revert_post": {
|
||
"properties": {
|
||
"csrf_token": {
|
||
"title": "Csrf Token",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"csrf_token"
|
||
],
|
||
"title": "Body_revert_swap_swaps__swap_id__revert_post",
|
||
"type": "object"
|
||
},
|
||
"HTTPValidationError": {
|
||
"properties": {
|
||
"detail": {
|
||
"items": {
|
||
"$ref": "#/components/schemas/ValidationError"
|
||
},
|
||
"title": "Detail",
|
||
"type": "array"
|
||
}
|
||
},
|
||
"title": "HTTPValidationError",
|
||
"type": "object"
|
||
},
|
||
"ValidationError": {
|
||
"properties": {
|
||
"ctx": {
|
||
"title": "Context",
|
||
"type": "object"
|
||
},
|
||
"input": {
|
||
"title": "Input"
|
||
},
|
||
"loc": {
|
||
"items": {
|
||
"anyOf": [
|
||
{
|
||
"type": "string"
|
||
},
|
||
{
|
||
"type": "integer"
|
||
}
|
||
]
|
||
},
|
||
"title": "Location",
|
||
"type": "array"
|
||
},
|
||
"msg": {
|
||
"title": "Message",
|
||
"type": "string"
|
||
},
|
||
"type": {
|
||
"title": "Error Type",
|
||
"type": "string"
|
||
}
|
||
},
|
||
"required": [
|
||
"loc",
|
||
"msg",
|
||
"type"
|
||
],
|
||
"title": "ValidationError",
|
||
"type": "object"
|
||
}
|
||
}
|
||
},
|
||
"info": {
|
||
"title": "Contínuos CRR",
|
||
"version": "0.1.0"
|
||
},
|
||
"openapi": "3.1.0",
|
||
"paths": {
|
||
"/": {
|
||
"get": {
|
||
"operationId": "dashboard__get",
|
||
"parameters": [
|
||
{
|
||
"in": "query",
|
||
"name": "year",
|
||
"required": false,
|
||
"schema": {
|
||
"anyOf": [
|
||
{
|
||
"type": "integer"
|
||
},
|
||
{
|
||
"type": "null"
|
||
}
|
||
],
|
||
"title": "Year"
|
||
}
|
||
},
|
||
{
|
||
"in": "query",
|
||
"name": "month",
|
||
"required": false,
|
||
"schema": {
|
||
"anyOf": [
|
||
{
|
||
"type": "integer"
|
||
},
|
||
{
|
||
"type": "null"
|
||
}
|
||
],
|
||
"title": "Month"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Dashboard"
|
||
}
|
||
},
|
||
"/account": {
|
||
"get": {
|
||
"description": "The password editor lives on its own page, reached by clicking the user name.",
|
||
"operationId": "account_page_account_get",
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
}
|
||
},
|
||
"summary": "Account Page"
|
||
},
|
||
"post": {
|
||
"description": "A user changes its own password from `/account`: one field, no ceremony.",
|
||
"operationId": "account_password_account_post",
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_account_password_account_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Account Password"
|
||
}
|
||
},
|
||
"/admin/assign": {
|
||
"get": {
|
||
"description": "Pick a day and put someone on one of its shifts. No change request involved.\n\n`on` is a whole ISO date (`YYYY-MM-DD`), which is what the date input sends.",
|
||
"operationId": "admin_assign_day_admin_assign_get",
|
||
"parameters": [
|
||
{
|
||
"in": "query",
|
||
"name": "on",
|
||
"required": false,
|
||
"schema": {
|
||
"anyOf": [
|
||
{
|
||
"type": "string"
|
||
},
|
||
{
|
||
"type": "null"
|
||
}
|
||
],
|
||
"title": "On"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Assign Day"
|
||
},
|
||
"post": {
|
||
"operationId": "admin_assign_team_admin_assign_post",
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_assign_team_admin_assign_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Assign Team"
|
||
}
|
||
},
|
||
"/admin/month": {
|
||
"get": {
|
||
"operationId": "admin_month_admin_month_get",
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
}
|
||
},
|
||
"summary": "Admin Month"
|
||
}
|
||
},
|
||
"/admin/notifications": {
|
||
"get": {
|
||
"operationId": "admin_notifications_admin_notifications_get",
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
}
|
||
},
|
||
"summary": "Admin Notifications"
|
||
}
|
||
},
|
||
"/admin/rotation/{member_id}/move": {
|
||
"post": {
|
||
"operationId": "admin_rotation_move_admin_rotation__member_id__move_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "member_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Member Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_rotation_move_admin_rotation__member_id__move_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Rotation Move"
|
||
}
|
||
},
|
||
"/admin/rotation/{member_id}/remove": {
|
||
"post": {
|
||
"operationId": "admin_rotation_remove_admin_rotation__member_id__remove_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "member_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Member Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_rotation_remove_admin_rotation__member_id__remove_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Rotation Remove"
|
||
}
|
||
},
|
||
"/admin/schedules": {
|
||
"get": {
|
||
"operationId": "admin_schedules_admin_schedules_get",
|
||
"parameters": [
|
||
{
|
||
"in": "query",
|
||
"name": "schedule_id",
|
||
"required": false,
|
||
"schema": {
|
||
"anyOf": [
|
||
{
|
||
"type": "integer"
|
||
},
|
||
{
|
||
"type": "null"
|
||
}
|
||
],
|
||
"title": "Schedule Id"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Schedules"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}": {
|
||
"get": {
|
||
"operationId": "admin_schedule_admin_schedules__schedule_id__get",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "query",
|
||
"name": "year",
|
||
"required": false,
|
||
"schema": {
|
||
"anyOf": [
|
||
{
|
||
"type": "integer"
|
||
},
|
||
{
|
||
"type": "null"
|
||
}
|
||
],
|
||
"title": "Year"
|
||
}
|
||
},
|
||
{
|
||
"in": "query",
|
||
"name": "month",
|
||
"required": false,
|
||
"schema": {
|
||
"anyOf": [
|
||
{
|
||
"type": "integer"
|
||
},
|
||
{
|
||
"type": "null"
|
||
}
|
||
],
|
||
"title": "Month"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Schedule"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}/apply-pattern": {
|
||
"post": {
|
||
"operationId": "admin_apply_pattern_admin_schedules__schedule_id__apply_pattern_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_apply_pattern_admin_schedules__schedule_id__apply_pattern_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Apply Pattern"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}/apply-rotation": {
|
||
"post": {
|
||
"operationId": "admin_apply_rotation_admin_schedules__schedule_id__apply_rotation_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_apply_rotation_admin_schedules__schedule_id__apply_rotation_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Apply Rotation"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}/assignments": {
|
||
"post": {
|
||
"operationId": "admin_schedule_assignments_admin_schedules__schedule_id__assignments_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Schedule Assignments"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}/assignments/{assignment_id}/regenerate": {
|
||
"post": {
|
||
"operationId": "admin_assignment_regenerate_admin_schedules__schedule_id__assignments__assignment_id__regenerate_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "path",
|
||
"name": "assignment_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Assignment Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_assignment_regenerate_admin_schedules__schedule_id__assignments__assignment_id__regenerate_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Assignment Regenerate"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}/assignments/{assignment_id}/use-rotation": {
|
||
"post": {
|
||
"operationId": "admin_assignment_regenerate_admin_schedules__schedule_id__assignments__assignment_id__use_rotation_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "path",
|
||
"name": "assignment_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Assignment Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_assignment_regenerate_admin_schedules__schedule_id__assignments__assignment_id__use_rotation_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Assignment Regenerate"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}/clear-from": {
|
||
"post": {
|
||
"operationId": "admin_clear_from_admin_schedules__schedule_id__clear_from_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_clear_from_admin_schedules__schedule_id__clear_from_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Clear From"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}/pattern": {
|
||
"post": {
|
||
"operationId": "admin_schedule_pattern_admin_schedules__schedule_id__pattern_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Schedule Pattern"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}/rotation": {
|
||
"post": {
|
||
"operationId": "admin_rotation_add_admin_schedules__schedule_id__rotation_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_rotation_add_admin_schedules__schedule_id__rotation_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Rotation Add"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}/settings": {
|
||
"post": {
|
||
"operationId": "admin_schedule_settings_admin_schedules__schedule_id__settings_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_schedule_settings_admin_schedules__schedule_id__settings_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Schedule Settings"
|
||
}
|
||
},
|
||
"/admin/schedules/{schedule_id}/swaps/{swap_id}/assign": {
|
||
"post": {
|
||
"description": "Complete a change request on behalf of the rota, including for rota-only teams.",
|
||
"operationId": "admin_schedule_assign_swap_admin_schedules__schedule_id__swaps__swap_id__assign_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "path",
|
||
"name": "swap_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Swap Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_schedule_assign_swap_admin_schedules__schedule_id__swaps__swap_id__assign_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Schedule Assign Swap"
|
||
}
|
||
},
|
||
"/admin/teams": {
|
||
"get": {
|
||
"description": "Who works the rota. Users are not created here: a team only picks who signs in for it.",
|
||
"operationId": "admin_teams_admin_teams_get",
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
}
|
||
},
|
||
"summary": "Admin Teams"
|
||
},
|
||
"post": {
|
||
"description": "Add a team to the rota. The user who signs in for it is chosen here or later.",
|
||
"operationId": "admin_create_team_admin_teams_post",
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_create_team_admin_teams_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Create Team"
|
||
}
|
||
},
|
||
"/admin/teams/{team_id}/delete": {
|
||
"get": {
|
||
"operationId": "admin_team_delete_page_admin_teams__team_id__delete_get",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "team_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Team Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Team Delete Page"
|
||
},
|
||
"post": {
|
||
"operationId": "admin_team_delete_admin_teams__team_id__delete_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "team_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Team Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_team_delete_admin_teams__team_id__delete_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Team Delete"
|
||
}
|
||
},
|
||
"/admin/teams/{team_id}/update": {
|
||
"post": {
|
||
"operationId": "admin_update_team_admin_teams__team_id__update_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "team_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Team Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_update_team_admin_teams__team_id__update_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Update Team"
|
||
}
|
||
},
|
||
"/admin/users": {
|
||
"get": {
|
||
"description": "The users: created here, then assigned to the teams they sign in for.",
|
||
"operationId": "admin_users_admin_users_get",
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
}
|
||
},
|
||
"summary": "Admin Users"
|
||
},
|
||
"post": {
|
||
"description": "A new user, assigned to the teams it will sign in for.",
|
||
"operationId": "admin_create_user_admin_users_post",
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_create_user_admin_users_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Create User"
|
||
}
|
||
},
|
||
"/admin/users/{user_id}/delete": {
|
||
"post": {
|
||
"operationId": "admin_user_delete_admin_users__user_id__delete_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "user_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "User Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_user_delete_admin_users__user_id__delete_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin User Delete"
|
||
}
|
||
},
|
||
"/admin/users/{user_id}/update": {
|
||
"post": {
|
||
"operationId": "admin_update_user_admin_users__user_id__update_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "user_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "User Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_update_user_admin_users__user_id__update_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Update User"
|
||
}
|
||
},
|
||
"/assignments/{assignment_id}/swap": {
|
||
"post": {
|
||
"operationId": "create_swap_assignments__assignment_id__swap_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "assignment_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Assignment Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_create_swap_assignments__assignment_id__swap_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Create Swap"
|
||
}
|
||
},
|
||
"/calendar": {
|
||
"get": {
|
||
"description": "The links to subscribe, kept off the rota so that page stays about the shifts.",
|
||
"operationId": "calendar_page_calendar_get",
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
}
|
||
},
|
||
"summary": "Calendar Page"
|
||
}
|
||
},
|
||
"/calendar/{token}.ics": {
|
||
"get": {
|
||
"description": "Subscribe from any calendar app: one feed per team, plus a shared whole-rota feed.",
|
||
"operationId": "calendar_feed_calendar__token__ics_get",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "token",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Token",
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Calendar Feed"
|
||
}
|
||
},
|
||
"/export/schedule": {
|
||
"get": {
|
||
"description": "Choose a schedule and exact dates before downloading its PDF.",
|
||
"operationId": "export_schedule_export_schedule_get",
|
||
"parameters": [
|
||
{
|
||
"in": "query",
|
||
"name": "schedule_id",
|
||
"required": false,
|
||
"schema": {
|
||
"anyOf": [
|
||
{
|
||
"type": "integer"
|
||
},
|
||
{
|
||
"type": "null"
|
||
}
|
||
],
|
||
"title": "Schedule Id"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Export Schedule"
|
||
}
|
||
},
|
||
"/export/schedule.pdf": {
|
||
"get": {
|
||
"operationId": "download_schedule_pdf_export_schedule_pdf_get",
|
||
"parameters": [
|
||
{
|
||
"in": "query",
|
||
"name": "schedule_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Schedule Id",
|
||
"type": "integer"
|
||
}
|
||
},
|
||
{
|
||
"in": "query",
|
||
"name": "start_date",
|
||
"required": true,
|
||
"schema": {
|
||
"format": "date",
|
||
"title": "Start Date",
|
||
"type": "string"
|
||
}
|
||
},
|
||
{
|
||
"in": "query",
|
||
"name": "end_date",
|
||
"required": true,
|
||
"schema": {
|
||
"format": "date",
|
||
"title": "End Date",
|
||
"type": "string"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Download Schedule Pdf"
|
||
}
|
||
},
|
||
"/health": {
|
||
"get": {
|
||
"operationId": "health_health_get",
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
}
|
||
},
|
||
"summary": "Health"
|
||
}
|
||
},
|
||
"/login": {
|
||
"get": {
|
||
"operationId": "login_page_login_get",
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
}
|
||
},
|
||
"summary": "Login Page"
|
||
},
|
||
"post": {
|
||
"operationId": "login_login_post",
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_login_login_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Login"
|
||
}
|
||
},
|
||
"/logout": {
|
||
"post": {
|
||
"operationId": "logout_logout_post",
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_logout_logout_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Logout"
|
||
}
|
||
},
|
||
"/swaps": {
|
||
"get": {
|
||
"operationId": "swaps_page_swaps_get",
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
}
|
||
},
|
||
"summary": "Swaps Page"
|
||
}
|
||
},
|
||
"/swaps/new": {
|
||
"get": {
|
||
"description": "Its own page, so the rota keeps showing only who works each day.",
|
||
"operationId": "new_swap_page_swaps_new_get",
|
||
"parameters": [
|
||
{
|
||
"in": "query",
|
||
"name": "year",
|
||
"required": false,
|
||
"schema": {
|
||
"anyOf": [
|
||
{
|
||
"type": "integer"
|
||
},
|
||
{
|
||
"type": "null"
|
||
}
|
||
],
|
||
"title": "Year"
|
||
}
|
||
},
|
||
{
|
||
"in": "query",
|
||
"name": "month",
|
||
"required": false,
|
||
"schema": {
|
||
"anyOf": [
|
||
{
|
||
"type": "integer"
|
||
},
|
||
{
|
||
"type": "null"
|
||
}
|
||
],
|
||
"title": "Month"
|
||
}
|
||
}
|
||
],
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"text/html": {
|
||
"schema": {
|
||
"type": "string"
|
||
}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "New Swap Page"
|
||
},
|
||
"post": {
|
||
"operationId": "create_swap_for_chosen_shift_swaps_new_post",
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_create_swap_for_chosen_shift_swaps_new_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Create Swap For Chosen Shift"
|
||
}
|
||
},
|
||
"/swaps/{swap_id}/accept": {
|
||
"post": {
|
||
"operationId": "accept_swap_swaps__swap_id__accept_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "swap_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Swap Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_accept_swap_swaps__swap_id__accept_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Accept Swap"
|
||
}
|
||
},
|
||
"/swaps/{swap_id}/admin-reject": {
|
||
"post": {
|
||
"operationId": "admin_reject_swap_swaps__swap_id__admin_reject_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "swap_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Swap Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_admin_reject_swap_swaps__swap_id__admin_reject_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Admin Reject Swap"
|
||
}
|
||
},
|
||
"/swaps/{swap_id}/approve": {
|
||
"post": {
|
||
"operationId": "approve_swap_swaps__swap_id__approve_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "swap_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Swap Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_approve_swap_swaps__swap_id__approve_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Approve Swap"
|
||
}
|
||
},
|
||
"/swaps/{swap_id}/cancel": {
|
||
"post": {
|
||
"operationId": "cancel_swap_swaps__swap_id__cancel_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "swap_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Swap Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_cancel_swap_swaps__swap_id__cancel_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Cancel Swap"
|
||
}
|
||
},
|
||
"/swaps/{swap_id}/reject": {
|
||
"post": {
|
||
"operationId": "reject_swap_swaps__swap_id__reject_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "swap_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Swap Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_reject_swap_swaps__swap_id__reject_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Reject Swap"
|
||
}
|
||
},
|
||
"/swaps/{swap_id}/revert": {
|
||
"post": {
|
||
"description": "Administrator only: undo an approved swap and give both shifts back.",
|
||
"operationId": "revert_swap_swaps__swap_id__revert_post",
|
||
"parameters": [
|
||
{
|
||
"in": "path",
|
||
"name": "swap_id",
|
||
"required": true,
|
||
"schema": {
|
||
"title": "Swap Id",
|
||
"type": "integer"
|
||
}
|
||
}
|
||
],
|
||
"requestBody": {
|
||
"content": {
|
||
"application/x-www-form-urlencoded": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/Body_revert_swap_swaps__swap_id__revert_post"
|
||
}
|
||
}
|
||
},
|
||
"required": true
|
||
},
|
||
"responses": {
|
||
"200": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {}
|
||
}
|
||
},
|
||
"description": "Successful Response"
|
||
},
|
||
"422": {
|
||
"content": {
|
||
"application/json": {
|
||
"schema": {
|
||
"$ref": "#/components/schemas/HTTPValidationError"
|
||
}
|
||
}
|
||
},
|
||
"description": "Validation Error"
|
||
}
|
||
},
|
||
"summary": "Revert Swap"
|
||
}
|
||
}
|
||
}
|
||
},
|
||
"orm": {
|
||
"relationships": {
|
||
"Assignment": {
|
||
"schedule": {
|
||
"back_populates": null,
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"assignments.schedule_id",
|
||
"schedules.id"
|
||
]
|
||
],
|
||
"target": "Schedule",
|
||
"uselist": false
|
||
},
|
||
"team": {
|
||
"back_populates": null,
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"assignments.team_id",
|
||
"teams.id"
|
||
]
|
||
],
|
||
"target": "Team",
|
||
"uselist": false
|
||
}
|
||
},
|
||
"MonthlyPattern": {
|
||
"schedule": {
|
||
"back_populates": "pattern_days",
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"monthly_patterns.schedule_id",
|
||
"schedules.id"
|
||
]
|
||
],
|
||
"target": "Schedule",
|
||
"uselist": false
|
||
},
|
||
"team": {
|
||
"back_populates": null,
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"monthly_patterns.team_id",
|
||
"teams.id"
|
||
]
|
||
],
|
||
"target": "Team",
|
||
"uselist": false
|
||
}
|
||
},
|
||
"NotificationLog": {
|
||
"team": {
|
||
"back_populates": null,
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"notification_logs.team_id",
|
||
"teams.id"
|
||
]
|
||
],
|
||
"target": "Team",
|
||
"uselist": false
|
||
}
|
||
},
|
||
"RotationMember": {
|
||
"schedule": {
|
||
"back_populates": "rotation_members",
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"rotation_members.schedule_id",
|
||
"schedules.id"
|
||
]
|
||
],
|
||
"target": "Schedule",
|
||
"uselist": false
|
||
},
|
||
"team": {
|
||
"back_populates": null,
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"rotation_members.team_id",
|
||
"teams.id"
|
||
]
|
||
],
|
||
"target": "Team",
|
||
"uselist": false
|
||
}
|
||
},
|
||
"Schedule": {
|
||
"pattern_days": {
|
||
"back_populates": "schedule",
|
||
"cascade": [
|
||
"delete",
|
||
"delete-orphan",
|
||
"expunge",
|
||
"merge",
|
||
"refresh-expire",
|
||
"save-update"
|
||
],
|
||
"order_by": [
|
||
"monthly_patterns.day_of_month"
|
||
],
|
||
"pairs": [
|
||
[
|
||
"schedules.id",
|
||
"monthly_patterns.schedule_id"
|
||
]
|
||
],
|
||
"target": "MonthlyPattern",
|
||
"uselist": true
|
||
},
|
||
"rotation_members": {
|
||
"back_populates": "schedule",
|
||
"cascade": [
|
||
"delete",
|
||
"delete-orphan",
|
||
"expunge",
|
||
"merge",
|
||
"refresh-expire",
|
||
"save-update"
|
||
],
|
||
"order_by": [
|
||
"rotation_members.position"
|
||
],
|
||
"pairs": [
|
||
[
|
||
"schedules.id",
|
||
"rotation_members.schedule_id"
|
||
]
|
||
],
|
||
"target": "RotationMember",
|
||
"uselist": true
|
||
}
|
||
},
|
||
"SwapRequest": {
|
||
"accepted_by": {
|
||
"back_populates": null,
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"swap_requests.accepted_team_id",
|
||
"teams.id"
|
||
]
|
||
],
|
||
"target": "Team",
|
||
"uselist": false
|
||
},
|
||
"assignment": {
|
||
"back_populates": null,
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"swap_requests.assignment_id",
|
||
"assignments.id"
|
||
]
|
||
],
|
||
"target": "Assignment",
|
||
"uselist": false
|
||
},
|
||
"requester": {
|
||
"back_populates": null,
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"swap_requests.requester_id",
|
||
"teams.id"
|
||
]
|
||
],
|
||
"target": "Team",
|
||
"uselist": false
|
||
},
|
||
"target_assignment": {
|
||
"back_populates": null,
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"swap_requests.target_assignment_id",
|
||
"assignments.id"
|
||
]
|
||
],
|
||
"target": "Assignment",
|
||
"uselist": false
|
||
},
|
||
"target_team": {
|
||
"back_populates": null,
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"swap_requests.target_team_id",
|
||
"teams.id"
|
||
]
|
||
],
|
||
"target": "Team",
|
||
"uselist": false
|
||
}
|
||
},
|
||
"Team": {
|
||
"user": {
|
||
"back_populates": "teams",
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"teams.user_id",
|
||
"users.id"
|
||
]
|
||
],
|
||
"target": "User",
|
||
"uselist": false
|
||
}
|
||
},
|
||
"User": {
|
||
"teams": {
|
||
"back_populates": "user",
|
||
"cascade": [
|
||
"merge",
|
||
"save-update"
|
||
],
|
||
"order_by": [],
|
||
"pairs": [
|
||
[
|
||
"users.id",
|
||
"teams.user_id"
|
||
]
|
||
],
|
||
"target": "Team",
|
||
"uselist": true
|
||
}
|
||
}
|
||
},
|
||
"tables": {
|
||
"assignments": {
|
||
"columns": [
|
||
{
|
||
"default": null,
|
||
"name": "id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": true,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "schedule_id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "date",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "DATE"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "team_id",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": "generated",
|
||
"name": "source",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(20)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "note",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(500)"
|
||
},
|
||
{
|
||
"default": {
|
||
"callable": "datetime.utcnow"
|
||
},
|
||
"name": "updated_at",
|
||
"nullable": false,
|
||
"onupdate": {
|
||
"callable": "datetime.utcnow"
|
||
},
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "DATETIME"
|
||
}
|
||
],
|
||
"constraints": [
|
||
{
|
||
"columns": [
|
||
"id"
|
||
],
|
||
"name": null,
|
||
"type": "PrimaryKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"schedule_id",
|
||
"date"
|
||
],
|
||
"name": "uq_assignment_schedule_date",
|
||
"type": "UniqueConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"schedule_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "CASCADE",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"schedules.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"team_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "SET NULL",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"teams.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
}
|
||
],
|
||
"indexes": [
|
||
{
|
||
"columns": [
|
||
"date"
|
||
],
|
||
"name": "ix_assignments_date",
|
||
"unique": false
|
||
}
|
||
]
|
||
},
|
||
"monthly_patterns": {
|
||
"columns": [
|
||
{
|
||
"default": null,
|
||
"name": "id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": true,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "schedule_id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "day_of_month",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "team_id",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
}
|
||
],
|
||
"constraints": [
|
||
{
|
||
"columns": [
|
||
"id"
|
||
],
|
||
"name": null,
|
||
"type": "PrimaryKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"schedule_id",
|
||
"day_of_month"
|
||
],
|
||
"name": "uq_pattern_schedule_day",
|
||
"type": "UniqueConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"schedule_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "CASCADE",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"schedules.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"team_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "SET NULL",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"teams.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
},
|
||
{
|
||
"name": "ck_pattern_day_of_month",
|
||
"sql": "day_of_month BETWEEN 1 AND 31",
|
||
"type": "CheckConstraint"
|
||
}
|
||
],
|
||
"indexes": []
|
||
},
|
||
"notification_logs": {
|
||
"columns": [
|
||
{
|
||
"default": null,
|
||
"name": "id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": true,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "team_id",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "event_type",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(60)"
|
||
},
|
||
{
|
||
"default": "email",
|
||
"name": "channel",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(20)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "recipient",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(254)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "subject",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(250)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "body",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "TEXT"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "status",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(20)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "error",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "TEXT"
|
||
},
|
||
{
|
||
"default": {
|
||
"callable": "datetime.utcnow"
|
||
},
|
||
"name": "created_at",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "DATETIME"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "sent_at",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "DATETIME"
|
||
}
|
||
],
|
||
"constraints": [
|
||
{
|
||
"columns": [
|
||
"id"
|
||
],
|
||
"name": null,
|
||
"type": "PrimaryKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"team_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "SET NULL",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"teams.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
}
|
||
],
|
||
"indexes": []
|
||
},
|
||
"rotation_members": {
|
||
"columns": [
|
||
{
|
||
"default": null,
|
||
"name": "id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": true,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "schedule_id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "team_id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "position",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
}
|
||
],
|
||
"constraints": [
|
||
{
|
||
"columns": [
|
||
"id"
|
||
],
|
||
"name": null,
|
||
"type": "PrimaryKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"schedule_id",
|
||
"position"
|
||
],
|
||
"name": "uq_rotation_schedule_position",
|
||
"type": "UniqueConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"schedule_id",
|
||
"team_id"
|
||
],
|
||
"name": "uq_rotation_schedule_team",
|
||
"type": "UniqueConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"schedule_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "CASCADE",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"schedules.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"team_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "CASCADE",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"teams.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
}
|
||
],
|
||
"indexes": []
|
||
},
|
||
"schedules": {
|
||
"columns": [
|
||
{
|
||
"default": null,
|
||
"name": "id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": true,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "name",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(120)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "slug",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(80)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "schedule_type",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(20)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "weekdays",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(30)"
|
||
},
|
||
{
|
||
"default": "20:30:00",
|
||
"name": "start_time",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "TIME"
|
||
},
|
||
{
|
||
"default": "23:59:00",
|
||
"name": "end_time",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "TIME"
|
||
},
|
||
{
|
||
"default": "True",
|
||
"name": "requires_manager_approval",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "BOOLEAN"
|
||
},
|
||
{
|
||
"default": "True",
|
||
"name": "is_active",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "BOOLEAN"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "rotation_anchor_date",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "DATE"
|
||
},
|
||
{
|
||
"default": {
|
||
"callable": "datetime.utcnow"
|
||
},
|
||
"name": "created_at",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "DATETIME"
|
||
}
|
||
],
|
||
"constraints": [
|
||
{
|
||
"columns": [
|
||
"id"
|
||
],
|
||
"name": null,
|
||
"type": "PrimaryKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"name"
|
||
],
|
||
"name": null,
|
||
"type": "UniqueConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"slug"
|
||
],
|
||
"name": null,
|
||
"type": "UniqueConstraint"
|
||
}
|
||
],
|
||
"indexes": []
|
||
},
|
||
"swap_requests": {
|
||
"columns": [
|
||
{
|
||
"default": null,
|
||
"name": "id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": true,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "assignment_id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "target_assignment_id",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "requester_id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "target_team_id",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "accepted_team_id",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": "open",
|
||
"name": "status",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(30)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "message",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(500)"
|
||
},
|
||
{
|
||
"default": {
|
||
"callable": "datetime.utcnow"
|
||
},
|
||
"name": "created_at",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "DATETIME"
|
||
},
|
||
{
|
||
"default": {
|
||
"callable": "datetime.utcnow"
|
||
},
|
||
"name": "updated_at",
|
||
"nullable": false,
|
||
"onupdate": {
|
||
"callable": "datetime.utcnow"
|
||
},
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "DATETIME"
|
||
}
|
||
],
|
||
"constraints": [
|
||
{
|
||
"columns": [
|
||
"accepted_team_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "SET NULL",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"teams.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"assignment_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "CASCADE",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"assignments.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"id"
|
||
],
|
||
"name": null,
|
||
"type": "PrimaryKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"requester_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "CASCADE",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"teams.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"target_assignment_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "SET NULL",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"assignments.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"target_team_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "SET NULL",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"teams.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
}
|
||
],
|
||
"indexes": []
|
||
},
|
||
"teams": {
|
||
"columns": [
|
||
{
|
||
"default": null,
|
||
"name": "id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": true,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "name",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(120)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "user_id",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "email",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(254)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "phone",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(40)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "calendar_token",
|
||
"nullable": true,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(64)"
|
||
},
|
||
{
|
||
"default": "True",
|
||
"name": "is_active",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "BOOLEAN"
|
||
},
|
||
{
|
||
"default": "True",
|
||
"name": "notify_email",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "BOOLEAN"
|
||
},
|
||
{
|
||
"default": {
|
||
"callable": "datetime.utcnow"
|
||
},
|
||
"name": "created_at",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "DATETIME"
|
||
}
|
||
],
|
||
"constraints": [
|
||
{
|
||
"columns": [
|
||
"calendar_token"
|
||
],
|
||
"name": null,
|
||
"type": "UniqueConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"email"
|
||
],
|
||
"name": null,
|
||
"type": "UniqueConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"id"
|
||
],
|
||
"name": null,
|
||
"type": "PrimaryKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"user_id"
|
||
],
|
||
"name": null,
|
||
"ondelete": "SET NULL",
|
||
"onupdate": null,
|
||
"targets": [
|
||
"users.id"
|
||
],
|
||
"type": "ForeignKeyConstraint"
|
||
}
|
||
],
|
||
"indexes": []
|
||
},
|
||
"users": {
|
||
"columns": [
|
||
{
|
||
"default": null,
|
||
"name": "id",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": true,
|
||
"server_default": null,
|
||
"type": "INTEGER"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "name",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(120)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "username",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(80)"
|
||
},
|
||
{
|
||
"default": null,
|
||
"name": "password_hash",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "VARCHAR(300)"
|
||
},
|
||
{
|
||
"default": "False",
|
||
"name": "is_admin",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "BOOLEAN"
|
||
},
|
||
{
|
||
"default": "True",
|
||
"name": "is_active",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "BOOLEAN"
|
||
},
|
||
{
|
||
"default": {
|
||
"callable": "datetime.utcnow"
|
||
},
|
||
"name": "created_at",
|
||
"nullable": false,
|
||
"onupdate": null,
|
||
"primary_key": false,
|
||
"server_default": null,
|
||
"type": "DATETIME"
|
||
}
|
||
],
|
||
"constraints": [
|
||
{
|
||
"columns": [
|
||
"id"
|
||
],
|
||
"name": null,
|
||
"type": "PrimaryKeyConstraint"
|
||
},
|
||
{
|
||
"columns": [
|
||
"username"
|
||
],
|
||
"name": null,
|
||
"type": "UniqueConstraint"
|
||
}
|
||
],
|
||
"indexes": []
|
||
}
|
||
}
|
||
},
|
||
"workflows": {
|
||
"": {
|
||
"requests": [
|
||
{
|
||
"body": "{\"status\":\"ok\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/health",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Entrar · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n </nav>\n \n <div class=\"account\"><a href=\"/login\">Entrar</a></div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<section class=\"auth-card\">\n <div class=\"auth-brand\" aria-hidden=\"true\">\n <img src=\"http://testserver/brand/assets/logo.png\" alt=\"\">\n </div>\n <h1>Entrar</h1>\n <p class=\"muted\">Use o seu utilizador e a sua palavra-passe da escala.</p>\n <form method=\"post\" action=\"/login\" class=\"stack\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <label>Utilizador\n <input name=\"username\" autocomplete=\"username\" required autofocus>\n </label>\n <label>Palavra-passe\n <input type=\"password\" name=\"password\" autocomplete=\"current-password\" required>\n </label>\n <button class=\"primary\" type=\"submit\">Entrar</button>\n </form>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/login",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/account",
|
||
"status": 401
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/teams",
|
||
"status": 401
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/users",
|
||
"status": 401
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/schedules",
|
||
"status": 401
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/swaps",
|
||
"status": 401
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"Token CSRF inválido\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "POST",
|
||
"path": "/login",
|
||
"status": 400
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/"
|
||
},
|
||
"method": "POST",
|
||
"path": "/login",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/"
|
||
},
|
||
"method": "POST",
|
||
"path": "/login",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/"
|
||
},
|
||
"method": "POST",
|
||
"path": "/login",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>A minha conta · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n <a href=\"/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n <div class=\"flash info\" role=\"status\">Bem-vindo(a), Equipa Norte, Equipa partilhada.</div>\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/\">Rota</a></p>\n <h1>A minha conta</h1>\n <p class=\"muted\">Membro a · @member-a</p>\n \n <p class=\"muted\">Este utilizador entra em nome de 2 equipa(s): Equipa Norte, Equipa partilhada. Vê e controla os turnos de todas.</p>\n \n </div>\n</div>\n\n<section class=\"panel\">\n <h2>Alterar palavra-passe</h2>\n <form method=\"post\" action=\"/account\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"password\" name=\"new_password\" minlength=\"4\" autocomplete=\"new-password\" required autofocus>\n <div><button class=\"primary\" type=\"submit\">Alterar palavra-passe</button></div>\n </form>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/account",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Calendário · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n <a href=\"/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/\">Rota</a></p>\n <h1>Calendário</h1>\n <p class=\"muted\">Os teus turnos na app de calendário do telemóvel. Subscreve uma vez: as alterações à rota aparecem automaticamente, quando a app atualizar o calendário.</p>\n </div>\n</div>\n\n<div class=\"stack gap-lg\">\n\n <section class=\"panel calendar-feed\">\n <h2>Turnos de Equipa Norte</h2>\n <div class=\"calendar-subscribe-actions\" aria-label=\"Adicionar Turnos de Equipa Norte\">\n <a class=\"button primary\" href=\"webcal://testserver/calendar/phase4-feed-a.ics\">iPhone / Apple Calendar</a>\n <a class=\"button secondary\" href=\"https://calendar.google.com/calendar/render?cid=http%3A%2F%2Ftestserver%2Fcalendar%2Fphase4-feed-a.ics\" target=\"_blank\" rel=\"noopener noreferrer\">Google Calendar / Android</a>\n <a class=\"button secondary\" href=\"https://outlook.live.com/calendar/0/addfromweb?url=http%3A%2F%2Ftestserver%2Fcalendar%2Fphase4-feed-a.ics&name=Turnos+de+Equipa+Norte\" target=\"_blank\" rel=\"noopener noreferrer\">Outlook</a>\n </div>\n <p class=\"muted\">No iPhone, confirma a subscrição na app Calendário. No Google e Outlook, adiciona pelo navegador à conta que usas no telemóvel. <a href=\"#calendar-help\">Ver os passos</a>.</p>\n <label for=\"calendar-url-1\">Endereço para subscrever noutra app</label>\n <div class=\"calendar-url-row\">\n <input id=\"calendar-url-1\" type=\"text\" readonly value=\"http://testserver/calendar/phase4-feed-a.ics\" inputmode=\"url\" autocapitalize=\"off\" spellcheck=\"false\">\n <button class=\"secondary\" type=\"button\" data-copy=\"http://testserver/calendar/phase4-feed-a.ics\">Copiar endereço</button>\n </div>\n <p class=\"calendar-copy-status muted\" role=\"status\" data-copy-status></p>\n </section>\n\n <section class=\"panel calendar-feed\">\n <h2>Turnos de Equipa partilhada</h2>\n <div class=\"calendar-subscribe-actions\" aria-label=\"Adicionar Turnos de Equipa partilhada\">\n <a class=\"button primary\" href=\"webcal://testserver/calendar/phase4-feed-c.ics\">iPhone / Apple Calendar</a>\n <a class=\"button secondary\" href=\"https://calendar.google.com/calendar/render?cid=http%3A%2F%2Ftestserver%2Fcalendar%2Fphase4-feed-c.ics\" target=\"_blank\" rel=\"noopener noreferrer\">Google Calendar / Android</a>\n <a class=\"button secondary\" href=\"https://outlook.live.com/calendar/0/addfromweb?url=http%3A%2F%2Ftestserver%2Fcalendar%2Fphase4-feed-c.ics&name=Turnos+de+Equipa+partilhada\" target=\"_blank\" rel=\"noopener noreferrer\">Outlook</a>\n </div>\n <p class=\"muted\">No iPhone, confirma a subscrição na app Calendário. No Google e Outlook, adiciona pelo navegador à conta que usas no telemóvel. <a href=\"#calendar-help\">Ver os passos</a>.</p>\n <label for=\"calendar-url-2\">Endereço para subscrever noutra app</label>\n <div class=\"calendar-url-row\">\n <input id=\"calendar-url-2\" type=\"text\" readonly value=\"http://testserver/calendar/phase4-feed-c.ics\" inputmode=\"url\" autocapitalize=\"off\" spellcheck=\"false\">\n <button class=\"secondary\" type=\"button\" data-copy=\"http://testserver/calendar/phase4-feed-c.ics\">Copiar endereço</button>\n </div>\n <p class=\"calendar-copy-status muted\" role=\"status\" data-copy-status></p>\n </section>\n\n</div>\n\n<section class=\"panel calendar-help top-space\" id=\"calendar-help\">\n <h2>Como adicionar no telemóvel</h2>\n <details>\n <summary>iPhone / iPad — Apple Calendar</summary>\n <ol>\n <li>Toca em <strong>iPhone / Apple Calendar</strong> no calendário que queres adicionar.</li>\n <li>Permite abrir a app Calendário e confirma <strong>Subscrever</strong>.</li>\n <li>Se o botão não abrir a app, copia o endereço. Na app Calendário, abre <strong>Calendários → Adicionar calendário → Adicionar calendário de assinatura</strong> e cola-o.</li>\n </ol>\n </details>\n <details>\n <summary>Android / Google Calendar</summary>\n <ol>\n <li>Toca em <strong>Google Calendar / Android</strong> e entra na conta Google que usas no telemóvel.</li>\n <li>O Google exige a versão para computador para adicionar subscrições. No navegador do telemóvel, ativa <strong>Site para computador</strong>; se não conseguires, abre esta página num computador.</li>\n <li>Confirma a adição. Se não aparecer essa opção, copia o endereço e, no Google Calendar, escolhe <strong>Outros calendários → + → A partir de URL</strong>, cola-o e adiciona o calendário.</li>\n <li>Na app Google Calendar, abre o menu e ativa o novo calendário. Se não aparecer, verifica as definições de sincronização.</li>\n </ol>\n </details>\n <details>\n <summary>Outlook</summary>\n <ol>\n <li>Toca em <strong>Outlook</strong> e entra na conta que usas na app.</li>\n <li>Confirma a subscrição no Outlook na Web. Se necessário, usa <strong>Adicionar calendário → Subscrever a partir da Web</strong> e cola o endereço copiado.</li>\n <li>Na app Outlook, seleciona o novo calendário nessa conta. Se a versão móvel não mostrar a opção de subscrever, usa o navegador em modo computador.</li>\n </ol>\n </details>\n <p class=\"muted\">Escolhe uma subscrição por endereço, em vez de importar um ficheiro .ics, para receber futuras alterações. A atualização depende da app e pode demorar algumas horas. Para Google e Outlook, o endereço tem de estar acessível pela Internet.</p>\n</section>\n\n<script>\n // Clipboard permissions can fail even on HTTPS; keep a mobile-friendly fallback.\n document.querySelectorAll(\"[data-copy]\").forEach(function (button) {\n button.addEventListener(\"click\", async function () {\n var url = button.dataset.copy;\n var field = button.parentElement.querySelector(\"input\");\n var status = button.closest(\".calendar-feed\").querySelector(\"[data-copy-status]\");\n var copied = false;\n if (navigator.clipboard && window.isSecureContext) {\n try {\n await navigator.clipboard.writeText(url);\n copied = true;\n } catch (error) {\n // Fall through to selection/copy when clipboard access is denied.\n }\n }\n if (!copied) {\n field.focus();\n field.select();\n field.setSelectionRange(0, field.value.length);\n try {\n copied = document.execCommand(\"copy\");\n } catch (error) {\n // The selected field still supports the phone's native Copy action.\n }\n }\n if (copied) {\n button.textContent = \"Copiado\";\n status.textContent = \"Endereço copiado. Cola-o na tua app de calendário.\";\n setTimeout(function () { button.textContent = \"Copiar endereço\"; }, 2000);\n } else {\n status.textContent = \"Endereço selecionado. Toque no campo e escolha «Copiar».\";\n }\n });\n });\n</script>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/calendar",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n <a href=\"/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div><h1>Pedidos de troca</h1></div>\n</div>\n\n<div class=\"stack gap-lg\">\n\n <div class=\"empty\">Ainda não há pedidos de troca.</div>\n\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/swaps",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Pedir troca · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n <a href=\"/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/swaps\">Trocas</a></p>\n <h1>Pedir troca</h1>\n <p class=\"muted\">Uma troca é entre duas equipas da mesma escala: cada uma fica com o turno da outra.</p>\n </div>\n <div class=\"month-nav\">\n <a class=\"button secondary\" href=\"/swaps/new?year=2029&month=12\">← Anterior</a>\n <a class=\"button secondary\" href=\"/swaps/new?year=2030&month=2\">Seguinte →</a>\n </div>\n</div>\n\n<div class=\"stack gap-lg\">\n\n <section class=\"panel\">\n <div class=\"row between wrap gap\">\n <h2>Rotação Phase 4</h2>\n <span class=\"muted\">Janeiro 2030</span>\n </div>\n <form method=\"post\" action=\"/swaps/new\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <label>O turno que passo\n <select name=\"assignment_id\" required>\n <option value=\"1\">sáb 05 jan · Equipa Norte</option><option value=\"3\">sáb 19 jan · Equipa Norte</option>\n </select>\n </label>\n <label>O turno que quero em troca\n <select name=\"target_assignment_id\" required>\n <option value=\"2\">sáb 12 jan · Equipa Sul</option><option value=\"4\">sáb 26 jan · Equipa Sul</option>\n </select>\n </label>\n <label>Mensagem <span class=\"muted\">(opcional)</span>\n <input name=\"message\" maxlength=\"500\" placeholder=\"Não consigo fazer este dia…\">\n </label>\n <div class=\"row end\"><button class=\"primary\" type=\"submit\">Enviar pedido</button></div>\n </form>\n </section>\n\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/swaps/new?year=2030&month=1",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n <a href=\"/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <h1>Janeiro 2030</h1>\n </div>\n <div class=\"month-nav\">\n \n <a class=\"button secondary\" href=\"/calendar\">Calendário</a>\n <a class=\"button primary\" href=\"/swaps/new?year=2030&month=1\">Pedir troca</a>\n \n <a class=\"button secondary\" href=\"/?year=2029&month=12\">← Anterior</a>\n <a class=\"button secondary\" href=\"/?year=2030&month=2\">Seguinte →</a>\n </div>\n</div>\n\n<div class=\"calendar-list\">\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Ter</strong>\n <span>1</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qua</strong>\n <span>2</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qui</strong>\n <span>3</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sex</strong>\n <span>4</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sáb</strong>\n <span>5</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Sábados</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment mine\">\n <div>\n <div class=\"assignment-title\">Rotação Phase 4</div>\n <div class=\"assignment-team\">\n Equipa Norte\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Dom</strong>\n <span>6</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Domingo</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Seg</strong>\n <span>7</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Ter</strong>\n <span>8</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qua</strong>\n <span>9</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qui</strong>\n <span>10</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sex</strong>\n <span>11</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sáb</strong>\n <span>12</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Sábados</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Rotação Phase 4</div>\n <div class=\"assignment-team\">\n Equipa Sul\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Dom</strong>\n <span>13</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Domingo</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Seg</strong>\n <span>14</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Ter</strong>\n <span>15</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qua</strong>\n <span>16</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qui</strong>\n <span>17</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sex</strong>\n <span>18</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sáb</strong>\n <span>19</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Sábados</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment mine\">\n <div>\n <div class=\"assignment-title\">Rotação Phase 4</div>\n <div class=\"assignment-team\">\n Equipa Norte\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Dom</strong>\n <span>20</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Domingo</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Seg</strong>\n <span>21</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Ter</strong>\n <span>22</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qua</strong>\n <span>23</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qui</strong>\n <span>24</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sex</strong>\n <span>25</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sáb</strong>\n <span>26</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Sábados</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Rotação Phase 4</div>\n <div class=\"assignment-team\">\n Equipa Sul\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Dom</strong>\n <span>27</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Domingo</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Seg</strong>\n <span>28</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Ter</strong>\n <span>29</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qua</strong>\n <span>30</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qui</strong>\n <span>31</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/?year=2030&month=1",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Exportar escala · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n <a href=\"/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/\">Rota</a></p>\n <h1>Exportar escala</h1>\n <p class=\"muted\">Escolhe uma escala e as datas. O PDF é descarregado numa única folha A4, com os dias e as equipas agrupados por mês.</p>\n </div>\n</div>\n<section class=\"panel\">\n \n<form class=\"stack\" method=\"get\" action=\"/export/schedule.pdf\">\n <label>Escala\n <select name=\"schedule_id\" required>\n \n <option value=\"1\" selected>Noite</option>\n \n <option value=\"2\" >Almoço - Sábados</option>\n \n <option value=\"3\" >Almoço - Domingo</option>\n \n <option value=\"4\" >Rotação Phase 4</option>\n \n </select>\n </label>\n <div class=\"form-grid\">\n <label>Data inicial\n <input type=\"date\" name=\"start_date\" value=\"2026-10-01\" min=\"2000-01-01\" max=\"2100-12-31\" required>\n </label>\n <label>Data final\n <input type=\"date\" name=\"end_date\" value=\"2026-10-31\" min=\"2000-01-01\" max=\"2100-12-31\" required>\n </label>\n </div>\n <p class=\"muted\">As datas inicial e final estão incluídas. Máximo de 12 meses; o tamanho do texto adapta-se ao intervalo.</p>\n <div class=\"row end\"><button class=\"primary\" type=\"submit\">Descarregar PDF</button></div>\n</form>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/export/schedule",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessária a permissão de administrador\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/users",
|
||
"status": 403
|
||
},
|
||
{
|
||
"body": "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Contínuos CRR//Rota//PT\r\nCALSCALE:GREGORIAN\r\nMETHOD:PUBLISH\r\nX-WR-CALNAME:Contínuos CRR — Equipa Norte\r\nX-WR-CALDESC:Contínuos CRR — 1 de agosto de 2026 a 31 de março de 2027\r\nREFRESH-INTERVAL;VALUE=DURATION:PT12H\r\nX-PUBLISHED-TTL:PT12H\r\nX-WR-TIMEZONE:Europe/Lisbon\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Lisbon\r\nX-LIC-LOCATION:Europe/Lisbon\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0000\r\nTZOFFSETTO:+0100\r\nTZNAME:WEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0000\r\nTZNAME:WET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nEND:VCALENDAR\r\n",
|
||
"headers": {
|
||
"cache-control": "private, max-age=300",
|
||
"content-disposition": "inline; filename=\"rota.ics\"",
|
||
"content-type": "text/calendar; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/calendar/phase4-feed-a.ics",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"Calendário não encontrado\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/calendar/not-a-token.ics",
|
||
"status": 404
|
||
},
|
||
{
|
||
"body": "CONTÍNUOS CRR\nRotação Phase 4\nJaneiro 2030\n 5\nEquipa Norte\n12\nEquipa Sul\n19\nEquipa Norte\n26\nEquipa Sul\n",
|
||
"headers": {
|
||
"cache-control": "private, no-store",
|
||
"content-disposition": "attachment; filename=\"escala-4-2030-01-01-2030-01-31.pdf\"",
|
||
"content-type": "application/pdf"
|
||
},
|
||
"method": "GET",
|
||
"path": "/export/schedule.pdf?schedule_id=4&start_date=2030-01-01&end_date=2030-01-31",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/swaps"
|
||
},
|
||
"method": "POST",
|
||
"path": "/assignments/1/swap",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/swaps"
|
||
},
|
||
"method": "POST",
|
||
"path": "/swaps/1/accept",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/swaps"
|
||
},
|
||
"method": "POST",
|
||
"path": "/swaps/1/approve",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/swaps"
|
||
},
|
||
"method": "POST",
|
||
"path": "/swaps/1/revert",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Equipas · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n \n \n <a href=\"/admin/schedules\">Gerir escalas</a>\n <a href=\"/admin/teams\">Equipas</a>\n <a href=\"/admin/users\">Utilizadores</a>\n <a href=\"/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n <div class=\"flash info\" role=\"status\">Troca revertida: os dois turnos voltaram a quem os tinha.</div>\n \n \n<div class=\"page-head\">\n <div>\n <h1>Equipas</h1>\n <p class=\"muted\">Quem entra na rota. Uma equipa é quem fica com um turno, e pode ser uma pessoa ou várias. Os utilizadores que entram na aplicação criam-se em <a href=\"/admin/users\">Utilizadores</a>; aqui escolhe-se apenas quem entra para cada equipa.</p>\n </div>\n</div>\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <h2>Adicionar equipa</h2>\n <button class=\"primary\" type=\"submit\" form=\"add-team\">Adicionar equipa</button>\n </div>\n <form id=\"add-team\" method=\"post\" action=\"/admin/teams\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" required></label>\n <label>Telefone<input name=\"phone\"></label>\n <label>E-mail<input type=\"email\" name=\"email\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Utilizador <span class=\"muted\">(opcional)</span></legend>\n <label>Entra com\n <select name=\"assigned_user_id\">\n <option value=\"\">Ninguém — só aparece na escala</option>\n <option value=\"2\">@member-a — Membro a</option><option value=\"3\">@member-b — Membro b</option><option value=\"1\">@phase4-admin — Administrador de teste</option>\n </select>\n </label>\n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"notify_email\" checked> Notificações por e-mail</label>\n </form>\n</section>\n<div class=\"stack gap-lg top-space\">\n\n<details class=\"panel\">\n <summary><strong>Administrador de teste</strong> <span class=\"muted\">@phase4-admin</span></summary>\n <form method=\"post\" action=\"/admin/teams/1/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Administrador de teste\" required></label>\n <label>Telefone<input name=\"phone\" value=\"\"></label>\n <label>E-mail<input type=\"email\" name=\"email\" value=\"admin@example.com\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Utilizador</legend>\n <label>Entra com\n <select name=\"assigned_user_id\">\n <option value=\"\">Ninguém — só aparece na escala</option>\n <option value=\"2\" >@member-a — Membro a</option><option value=\"3\" >@member-b — Membro b</option><option value=\"1\" selected>@phase4-admin — Administrador de teste</option>\n </select>\n </label>\n <p class=\"muted\">O mesmo utilizador pode entrar em várias equipas: vê e controla os turnos de todas elas.</p>\n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"notify_email\" checked> Notificações por e-mail</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativa</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n</details>\n\n<details class=\"panel\">\n <summary><strong>Equipa Norte</strong> <span class=\"muted\">@member-a · partilhada com 1</span></summary>\n <form method=\"post\" action=\"/admin/teams/2/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Equipa Norte\" required></label>\n <label>Telefone<input name=\"phone\" value=\"\"></label>\n <label>E-mail<input type=\"email\" name=\"email\" value=\"a@example.com\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Utilizador</legend>\n <label>Entra com\n <select name=\"assigned_user_id\">\n <option value=\"\">Ninguém — só aparece na escala</option>\n <option value=\"2\" selected>@member-a — Membro a</option><option value=\"3\" >@member-b — Membro b</option><option value=\"1\" >@phase4-admin — Administrador de teste</option>\n </select>\n </label>\n <p class=\"muted\">O mesmo utilizador pode entrar em várias equipas: vê e controla os turnos de todas elas.</p>\n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"notify_email\" checked> Notificações por e-mail</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativa</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n</details>\n\n<details class=\"panel\">\n <summary><strong>Equipa Sul</strong> <span class=\"muted\">@member-b</span></summary>\n <form method=\"post\" action=\"/admin/teams/3/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Equipa Sul\" required></label>\n <label>Telefone<input name=\"phone\" value=\"\"></label>\n <label>E-mail<input type=\"email\" name=\"email\" value=\"b@example.com\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Utilizador</legend>\n <label>Entra com\n <select name=\"assigned_user_id\">\n <option value=\"\">Ninguém — só aparece na escala</option>\n <option value=\"2\" >@member-a — Membro a</option><option value=\"3\" selected>@member-b — Membro b</option><option value=\"1\" >@phase4-admin — Administrador de teste</option>\n </select>\n </label>\n <p class=\"muted\">O mesmo utilizador pode entrar em várias equipas: vê e controla os turnos de todas elas.</p>\n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"notify_email\" checked> Notificações por e-mail</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativa</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n</details>\n\n<details class=\"panel\">\n <summary><strong>Equipa partilhada</strong> <span class=\"muted\">@member-a · partilhada com 1</span></summary>\n <form method=\"post\" action=\"/admin/teams/4/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Equipa partilhada\" required></label>\n <label>Telefone<input name=\"phone\" value=\"\"></label>\n <label>E-mail<input type=\"email\" name=\"email\" value=\"c@example.com\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Utilizador</legend>\n <label>Entra com\n <select name=\"assigned_user_id\">\n <option value=\"\">Ninguém — só aparece na escala</option>\n <option value=\"2\" selected>@member-a — Membro a</option><option value=\"3\" >@member-b — Membro b</option><option value=\"1\" >@phase4-admin — Administrador de teste</option>\n </select>\n </label>\n <p class=\"muted\">O mesmo utilizador pode entrar em várias equipas: vê e controla os turnos de todas elas.</p>\n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"notify_email\" checked> Notificações por e-mail</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativa</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n <div><a class=\"button danger\" href=\"/admin/teams/4/delete\">Remover</a></div>\n </form>\n</details>\n\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/teams",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Utilizadores · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n \n \n <a href=\"/admin/schedules\">Gerir escalas</a>\n <a href=\"/admin/teams\">Equipas</a>\n <a href=\"/admin/users\">Utilizadores</a>\n <a href=\"/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <h1>Utilizadores</h1>\n <p class=\"muted\">Cada utilizador tem utilizador e palavra-passe e pode entrar em várias equipas. Quem entra vê e controla os turnos de todas elas, e a administração é do utilizador, não da equipa.</p>\n </div>\n</div>\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <h2>Adicionar utilizador</h2>\n <button class=\"primary\" type=\"submit\" form=\"add-user\">Adicionar utilizador</button>\n </div>\n <form id=\"add-user\" method=\"post\" action=\"/admin/users\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome <span class=\"muted\">(ex.: Equipa de sábado)</span><input name=\"name\" required></label>\n <label>Utilizador<input name=\"username\" pattern=\"[A-Za-z0-9._-]+\" required></label>\n <label>Palavra-passe<input type=\"password\" name=\"password\" minlength=\"4\" required></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Equipas <span class=\"muted\">(pode escolher várias)</span></legend>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"1\"> Administrador de teste</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"2\"> Equipa Norte</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"3\"> Equipa Sul</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"4\"> Equipa partilhada</label>\n \n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_admin\"> Administrador</label>\n </form>\n</section>\n<div class=\"stack gap-lg top-space\">\n\n<details class=\"panel\">\n <summary><strong>@member-a</strong> <span class=\"muted\">Membro a · 2 equipa(s)</span></summary>\n <form method=\"post\" action=\"/admin/users/2/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Membro a\" required></label>\n <label>Utilizador<input name=\"username\" pattern=\"[A-Za-z0-9._-]+\" value=\"member-a\" required></label>\n <label>Nova palavra-passe <span class=\"muted\">(vazio mantém a atual)</span><input type=\"password\" name=\"password\" minlength=\"4\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Equipas que entra</legend>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"1\" > Administrador de teste</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"2\" checked> Equipa Norte</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"3\" > Equipa Sul</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"4\" checked> Equipa partilhada</label>\n \n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_admin\" > Administrador</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativo</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n \n <p class=\"muted\">Equipas: Equipa Norte, Equipa partilhada</p>\n \n</details>\n\n<details class=\"panel\">\n <summary><strong>@member-b</strong> <span class=\"muted\">Membro b · 1 equipa(s)</span></summary>\n <form method=\"post\" action=\"/admin/users/3/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Membro b\" required></label>\n <label>Utilizador<input name=\"username\" pattern=\"[A-Za-z0-9._-]+\" value=\"member-b\" required></label>\n <label>Nova palavra-passe <span class=\"muted\">(vazio mantém a atual)</span><input type=\"password\" name=\"password\" minlength=\"4\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Equipas que entra</legend>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"1\" > Administrador de teste</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"2\" > Equipa Norte</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"3\" checked> Equipa Sul</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"4\" > Equipa partilhada</label>\n \n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_admin\" > Administrador</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativo</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n \n <p class=\"muted\">Equipas: Equipa Sul</p>\n \n</details>\n\n<details class=\"panel\">\n <summary><strong>@phase4-admin</strong> <span class=\"muted\">Administrador de teste · administrador · 1 equipa(s)</span></summary>\n <form method=\"post\" action=\"/admin/users/1/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Administrador de teste\" required></label>\n <label>Utilizador<input name=\"username\" pattern=\"[A-Za-z0-9._-]+\" value=\"phase4-admin\" required></label>\n <label>Nova palavra-passe <span class=\"muted\">(vazio mantém a atual)</span><input type=\"password\" name=\"password\" minlength=\"4\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Equipas que entra</legend>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"1\" checked> Administrador de teste</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"2\" > Equipa Norte</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"3\" > Equipa Sul</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"4\" > Equipa partilhada</label>\n \n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_admin\" checked> Administrador</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativo</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n \n <p class=\"muted\">Equipas: Administrador de teste</p>\n \n</details>\n\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/users",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n \n \n <a href=\"/admin/schedules\">Gerir escalas</a>\n <a href=\"/admin/teams\">Equipas</a>\n <a href=\"/admin/users\">Utilizadores</a>\n <a href=\"/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <h1>Escalas</h1>\n <p class=\"muted\">Uma página por tipo de escala. A escala da noite é fixa e repete todos os meses; o almoço de sábado e o de domingo são rotações independentes.</p>\n </div>\n <a class=\"button primary\" href=\"/admin/assign\">Atribuir por dia</a>\n</div>\n<div class=\"stack gap\">\n\n<section class=\"panel\" id=\"export-pdf\">\n <h2>Exportar PDF</h2>\n <p class=\"muted\">Escolhe uma escala e as datas para descarregar a lista de dias e equipas numa única folha A4.</p>\n \n<form class=\"stack\" method=\"get\" action=\"/export/schedule.pdf\">\n <label>Escala\n <select name=\"schedule_id\" required>\n \n <option value=\"1\" selected>Noite</option>\n \n <option value=\"2\" >Almoço - Sábados</option>\n \n <option value=\"3\" >Almoço - Domingo</option>\n \n <option value=\"4\" >Rotação Phase 4</option>\n \n </select>\n </label>\n <div class=\"form-grid\">\n <label>Data inicial\n <input type=\"date\" name=\"start_date\" value=\"2026-10-01\" min=\"2000-01-01\" max=\"2100-12-31\" required>\n </label>\n <label>Data final\n <input type=\"date\" name=\"end_date\" value=\"2026-10-31\" min=\"2000-01-01\" max=\"2100-12-31\" required>\n </label>\n </div>\n <p class=\"muted\">As datas inicial e final estão incluídas. Máximo de 12 meses; o tamanho do texto adapta-se ao intervalo.</p>\n <div class=\"row end\"><button class=\"primary\" type=\"submit\">Descarregar PDF</button></div>\n</form>\n</section>\n\n\n\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <div>\n <h2>Noite</h2>\n <p class=\"muted\">Todos os dias · repete todos os meses · 20:30–00:00</p>\n </div>\n <div class=\"actions\">\n <a class=\"button secondary\" href=\"/admin/schedules?schedule_id=1#export-pdf\">Exportar PDF</a>\n <a class=\"button primary\" href=\"/admin/schedules/1\">Configurar</a>\n </div>\n </div>\n \n <h3>Padrão</h3>\n <ul class=\"link-list\">\n <li class=\"muted\">Ainda não há ninguém definido.</li>\n </ul>\n \n</section>\n\n\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <div>\n <h2>Almoço - Sábados</h2>\n <p class=\"muted\">Rotação · Sábado · 13:00–16:00</p>\n </div>\n <div class=\"actions\">\n <a class=\"button secondary\" href=\"/admin/schedules?schedule_id=2#export-pdf\">Exportar PDF</a>\n <a class=\"button primary\" href=\"/admin/schedules/2\">Configurar</a>\n </div>\n </div>\n \n <p class=\"muted\">Próximas datas</p>\n <p class=\"date-list\">sáb 10 out · sáb 17 out · sáb 24 out · sáb 31 out · sáb 07 nov · sáb 14 nov</p>\n <h3>Ordem da rotação</h3>\n <ol class=\"rotation-list\">\n <li class=\"muted\">Ainda não há membros.</li>\n </ol>\n \n</section>\n\n\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <div>\n <h2>Almoço - Domingo</h2>\n <p class=\"muted\">Rotação · Domingo · 13:00–16:00</p>\n </div>\n <div class=\"actions\">\n <a class=\"button secondary\" href=\"/admin/schedules?schedule_id=3#export-pdf\">Exportar PDF</a>\n <a class=\"button primary\" href=\"/admin/schedules/3\">Configurar</a>\n </div>\n </div>\n \n <p class=\"muted\">Próximas datas</p>\n <p class=\"date-list\">dom 11 out · dom 18 out · dom 25 out · dom 01 nov · dom 08 nov · dom 15 nov</p>\n <h3>Ordem da rotação</h3>\n <ol class=\"rotation-list\">\n <li class=\"muted\">Ainda não há membros.</li>\n </ol>\n \n</section>\n\n\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <div>\n <h2>Rotação Phase 4</h2>\n <p class=\"muted\">Rotação · Sábado · 12:00–14:00</p>\n </div>\n <div class=\"actions\">\n <a class=\"button secondary\" href=\"/admin/schedules?schedule_id=4#export-pdf\">Exportar PDF</a>\n <a class=\"button primary\" href=\"/admin/schedules/4\">Configurar</a>\n </div>\n </div>\n \n <p class=\"muted\">Próximas datas</p>\n <p class=\"date-list\">sáb 10 out · sáb 17 out · sáb 24 out · sáb 31 out · sáb 07 nov · sáb 14 nov</p>\n <h3>Ordem da rotação</h3>\n <ol class=\"rotation-list\">\n \n <li><span>Equipa Norte</span></li>\n \n <li><span>Equipa Sul</span></li>\n \n </ol>\n \n</section>\n\n<section class=\"panel\">\n <h2>Calendário da equipa</h2>\n <p class=\"muted\">Um único endereço com a rota completa, para qualquer calendário.</p>\n <p><a class=\"button secondary\" href=\"/calendar\">Adicionar ao calendário do telemóvel</a></p>\n <div class=\"inline-form\">\n <input type=\"text\" readonly value=\"http://testserver/calendar/phase4-whole-rota.ics\" aria-label=\"Endereço do calendário da equipa\">\n <button class=\"secondary\" type=\"button\" data-copy=\"http://testserver/calendar/phase4-whole-rota.ics\">Copiar</button>\n </div>\n</section>\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/schedules",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n \n \n <a href=\"/admin/schedules\">Gerir escalas</a>\n <a href=\"/admin/teams\">Equipas</a>\n <a href=\"/admin/users\">Utilizadores</a>\n <a href=\"/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n\n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/admin/schedules\">Escalas</a></p>\n <h1>Rotação Phase 4</h1>\n <p class=\"muted\">Rotação · Sábado · 12:00–14:00</p>\n </div>\n <form method=\"post\" action=\"/admin/schedules/4/settings\" class=\"inline-form\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <label>Das horas<input type=\"time\" name=\"start_time\" value=\"12:00\" required></label>\n <label>Às horas<input type=\"time\" name=\"end_time\" value=\"14:00\" required></label>\n <label class=\"check\"><input type=\"checkbox\" name=\"requires_manager_approval\" checked> Aprovação da gestão</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativa</label>\n <button class=\"secondary\" type=\"submit\">Guardar</button>\n </form>\n</div>\n<div class=\"stack gap-lg\">\n\n <section class=\"panel\">\n <div class=\"row between wrap gap\">\n <div><h3>Janeiro 2030</h3></div>\n <div class=\"month-nav\">\n <a class=\"button secondary\" href=\"/admin/schedules/4?year=2029&month=12\">← Anterior</a>\n <a class=\"button secondary\" href=\"/admin/schedules/4?year=2030&month=2\">Seguinte →</a>\n </div>\n </div>\n <form method=\"post\" action=\"/admin/schedules/4/apply-rotation\" class=\"inline-form\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"hidden\" name=\"year\" value=\"2030\"><input type=\"hidden\" name=\"month\" value=\"1\">\n <button class=\"secondary\" type=\"submit\">Aplicar rotação em Janeiro</button>\n </form>\n <p class=\"muted\">Deixa sem atribuição os turnos <strong>automáticos</strong> do mês e ano escolhidos e de todos os seguintes. Nenhum turno é apagado e as atribuições manuais e as trocas concluídas são mantidas. Use Limpar turnos antes de Aplicar rotação, para recalcular tudo a partir de um ponto.</p>\n <form method=\"post\" action=\"/admin/schedules/4/assignments\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"hidden\" name=\"year\" value=\"2030\"><input type=\"hidden\" name=\"month\" value=\"1\">\n <div class=\"table-wrap\"><table><thead><tr><th>Data</th><th>Equipa</th><th>Origem</th><th>Ação</th></tr></thead><tbody>\n \n <tr><td>sáb 05 jan</td><td><select name=\"assignment_1\"><option value=\"\">Sem atribuição</option><option value=\"1\" >Administrador de teste</option><option value=\"2\" selected>Equipa Norte</option><option value=\"3\" >Equipa Sul</option><option value=\"4\" >Equipa partilhada</option></select></td><td><span class=\"badge\">manual</span></td><td><button class=\"secondary small\" type=\"submit\" form=\"regenerate-1\" aria-label=\"Recalcular o turno de sáb 05 jan\">Recalcular</button></td></tr>\n \n <tr><td>sáb 12 jan</td><td><select name=\"assignment_2\"><option value=\"\">Sem atribuição</option><option value=\"1\" >Administrador de teste</option><option value=\"2\" >Equipa Norte</option><option value=\"3\" selected>Equipa Sul</option><option value=\"4\" >Equipa partilhada</option></select></td><td><span class=\"badge\">manual</span></td><td><button class=\"secondary small\" type=\"submit\" form=\"regenerate-2\" aria-label=\"Recalcular o turno de sáb 12 jan\">Recalcular</button></td></tr>\n \n <tr><td>sáb 19 jan</td><td><select name=\"assignment_3\"><option value=\"\">Sem atribuição</option><option value=\"1\" >Administrador de teste</option><option value=\"2\" selected>Equipa Norte</option><option value=\"3\" >Equipa Sul</option><option value=\"4\" >Equipa partilhada</option></select></td><td><span class=\"badge\">troca</span></td><td><button class=\"secondary small\" type=\"submit\" form=\"regenerate-3\" aria-label=\"Recalcular o turno de sáb 19 jan\">Recalcular</button></td></tr>\n \n <tr><td>sáb 26 jan</td><td><select name=\"assignment_4\"><option value=\"\">Sem atribuição</option><option value=\"1\" >Administrador de teste</option><option value=\"2\" >Equipa Norte</option><option value=\"3\" selected>Equipa Sul</option><option value=\"4\" >Equipa partilhada</option></select></td><td><span class=\"badge\">automático</span></td><td><button class=\"secondary small\" type=\"submit\" form=\"regenerate-4\" aria-label=\"Recalcular o turno de sáb 26 jan\">Recalcular</button></td></tr>\n \n </tbody></table></div>\n <div class=\"sticky-actions\"><button class=\"primary\" type=\"submit\">Guardar Janeiro</button></div>\n </form>\n \n <form id=\"regenerate-1\" method=\"post\" action=\"/admin/schedules/4/assignments/1/regenerate\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n </form>\n \n <form id=\"regenerate-2\" method=\"post\" action=\"/admin/schedules/4/assignments/2/regenerate\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n </form>\n \n <form id=\"regenerate-3\" method=\"post\" action=\"/admin/schedules/4/assignments/3/regenerate\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n </form>\n \n <form id=\"regenerate-4\" method=\"post\" action=\"/admin/schedules/4/assignments/4/regenerate\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n </form>\n \n </section>\n <section class=\"panel\">\n <h3>Próximas datas</h3>\n <p class=\"date-list\">sáb 10 out · sáb 17 out · sáb 24 out · sáb 31 out · sáb 07 nov · sáb 14 nov</p>\n </section>\n <section class=\"panel\" id=\"rotation-order\">\n <h3>Ordem da rotação</h3>\n <p class=\"muted\">Rotação independente, preenchida por ordem. Depois de alterar a ordem, use Aplicar rotação acima para atualizar os turnos automáticos deste mês.</p>\n <ol class=\"rotation-list\">\n \n <li>\n <span>Equipa Norte</span>\n <div class=\"actions compact\">\n <form method=\"post\" action=\"/admin/rotation/1/move\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><input type=\"hidden\" name=\"direction\" value=\"up\"><button class=\"secondary small\" type=\"submit\" aria-label=\"Subir Equipa Norte\">↑</button></form>\n <form method=\"post\" action=\"/admin/rotation/1/move\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><input type=\"hidden\" name=\"direction\" value=\"down\"><button class=\"secondary small\" type=\"submit\" aria-label=\"Descer Equipa Norte\">↓</button></form>\n <form method=\"post\" action=\"/admin/rotation/1/remove\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><button class=\"danger small\" type=\"submit\">Remover</button></form>\n </div>\n </li>\n \n <li>\n <span>Equipa Sul</span>\n <div class=\"actions compact\">\n <form method=\"post\" action=\"/admin/rotation/2/move\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><input type=\"hidden\" name=\"direction\" value=\"up\"><button class=\"secondary small\" type=\"submit\" aria-label=\"Subir Equipa Sul\">↑</button></form>\n <form method=\"post\" action=\"/admin/rotation/2/move\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><input type=\"hidden\" name=\"direction\" value=\"down\"><button class=\"secondary small\" type=\"submit\" aria-label=\"Descer Equipa Sul\">↓</button></form>\n <form method=\"post\" action=\"/admin/rotation/2/remove\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><button class=\"danger small\" type=\"submit\">Remover</button></form>\n </div>\n </li>\n \n </ol>\n <form method=\"post\" action=\"/admin/schedules/4/rotation\" class=\"inline-form\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <select name=\"team_id\" required><option value=\"\">Adicionar equipa…</option><option value=\"1\">Administrador de teste</option><option value=\"2\">Equipa Norte</option><option value=\"3\">Equipa Sul</option><option value=\"4\">Equipa partilhada</option></select>\n <button class=\"primary\" type=\"submit\">Adicionar à rotação</button>\n </form>\n </section>\n\n <section class=\"panel\">\n <h3>Limpar atribuições</h3>\n <p class=\"muted\">Limpar atribuições a partir do mês de:</p>\n <form method=\"post\" action=\"/admin/schedules/4/clear-from\" class=\"inline-form\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <label><select name=\"month\">\n <option value=\"1\" selected>Janeiro</option><option value=\"2\" >Fevereiro</option><option value=\"3\" >Março</option><option value=\"4\" >Abril</option><option value=\"5\" >Maio</option><option value=\"6\" >Junho</option><option value=\"7\" >Julho</option><option value=\"8\" >Agosto</option><option value=\"9\" >Setembro</option><option value=\"10\" >Outubro</option><option value=\"11\" >Novembro</option><option value=\"12\" >Dezembro</option>\n </select></label>\n <label><input type=\"number\" name=\"year\" value=\"2030\" min=\"2000\" max=\"2100\" required></label>\n <button class=\"danger\" type=\"submit\">Limpar turnos</button>\n </form>\n </section>\n <section class=\"panel\" id=\"swap-requests\">\n <h3>Pedidos de troca</h3>\n <p class=\"muted\">Um administrador pode atribuir o turno a outra equipa, mesmo que nenhuma pessoa entre na aplicação por ela. Só se aplica se quem pediu a troca continuar a ser o titular do turno.</p>\n <ul class=\"link-list\">\n \n <li class=\"muted\">Não há pedidos de troca nesta escala.</li>\n \n </ul>\n </section>\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/schedules/4?year=2030&month=1",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Atribuição por dia · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n \n \n <a href=\"/admin/schedules\">Gerir escalas</a>\n <a href=\"/admin/teams\">Equipas</a>\n <a href=\"/admin/users\">Utilizadores</a>\n <a href=\"/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/admin/schedules\">Escalas</a></p>\n <h1>Atribuição por dia</h1>\n <p class=\"muted\">Escolha o dia e a equipa que fica com cada turno. É uma atribuição direta, não uma troca.</p>\n </div>\n <form method=\"get\" action=\"/admin/assign\" class=\"inline-form\">\n <label>Dia<input type=\"date\" name=\"on\" value=\"2030-01-05\" min=\"1999-12-31\" max=\"2100-12-30\" required onchange=\"this.form.submit()\"></label>\n </form>\n</div>\n\n<div class=\"month-nav\">\n <a class=\"button secondary\" href=\"/admin/assign?on=2030-01-04\">← Dia anterior</a>\n <a class=\"button secondary \" href=\"/admin/assign?on=2026-10-07\">Hoje</a>\n <a class=\"button secondary\" href=\"/admin/assign?on=2030-01-06\">Dia seguinte →</a>\n</div>\n\n<div class=\"stack gap-lg top-space\">\n <section class=\"panel\">\n <h2>Sábado, 5 de janeiro de 2030</h2>\n \n <form method=\"post\" action=\"/admin/assign\" class=\"row between wrap gap top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"hidden\" name=\"assignment_id\" value=\"9\">\n <div>\n <strong>Noite</strong>\n <span class=\"muted\"> · 20:30–00:00</span>\n <div class=\"muted\">\n \n Agora: sem atribuição\n \n </div>\n </div>\n <div class=\"inline-form\">\n <select name=\"team_id\">\n <option value=\"auto\" selected>Automático → sem ninguém</option>\n <option value=\"\" >Sem atribuição</option>\n \n <option value=\"1\" >Administrador de teste</option>\n \n <option value=\"2\" >Equipa Norte</option>\n \n <option value=\"3\" >Equipa Sul</option>\n \n <option value=\"4\" >Equipa partilhada</option>\n \n </select>\n <button class=\"secondary small\" type=\"submit\">Atribuir</button>\n </div>\n </form>\n \n <form method=\"post\" action=\"/admin/assign\" class=\"row between wrap gap top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"hidden\" name=\"assignment_id\" value=\"36\">\n <div>\n <strong>Almoço - Sábados</strong>\n <span class=\"muted\"> · 13:00–16:00</span>\n <div class=\"muted\">\n \n Agora: sem atribuição\n \n </div>\n </div>\n <div class=\"inline-form\">\n <select name=\"team_id\">\n <option value=\"auto\" selected>Automático → sem ninguém</option>\n <option value=\"\" >Sem atribuição</option>\n \n <option value=\"1\" >Administrador de teste</option>\n \n <option value=\"2\" >Equipa Norte</option>\n \n <option value=\"3\" >Equipa Sul</option>\n \n <option value=\"4\" >Equipa partilhada</option>\n \n </select>\n <button class=\"secondary small\" type=\"submit\">Atribuir</button>\n </div>\n </form>\n \n <form method=\"post\" action=\"/admin/assign\" class=\"row between wrap gap top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"hidden\" name=\"assignment_id\" value=\"1\">\n <div>\n <strong>Rotação Phase 4</strong>\n <span class=\"muted\"> · 12:00–14:00</span>\n <div class=\"muted\">\n \n Agora: Equipa Norte (manual)\n \n </div>\n </div>\n <div class=\"inline-form\">\n <select name=\"team_id\">\n <option value=\"auto\" >Automático → Equipa Norte</option>\n <option value=\"\" >Sem atribuição</option>\n \n <option value=\"1\" >Administrador de teste</option>\n \n <option value=\"2\" selected>Equipa Norte</option>\n \n <option value=\"3\" >Equipa Sul</option>\n \n <option value=\"4\" >Equipa partilhada</option>\n \n </select>\n <button class=\"secondary small\" type=\"submit\">Atribuir</button>\n </div>\n </form>\n \n </section>\n <p class=\"muted\">Atribuir aqui fecha qualquer pedido de troca em aberto sobre esse turno e avisa a nova equipa e a anterior.</p>\n <p class=\"muted\">«Automático» repõe a equipa que a escala dá a este dia — a ordem da rotação ou o padrão de noites — e volta a marcar o turno como automático.</p>\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/assign?on=2030-01-05",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Registo de e-mails · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n \n \n <a href=\"/admin/schedules\">Gerir escalas</a>\n <a href=\"/admin/teams\">Equipas</a>\n <a href=\"/admin/users\">Utilizadores</a>\n <a href=\"/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\"><div><h1>Registo de e-mails</h1><p class=\"muted\">«ignorado» significa que a equipa não tem e-mail, que tem as notificações desligadas ou que o SMTP não está configurado.</p></div></div>\n<div class=\"table-wrap\"><table><thead><tr><th>Data e hora</th><th>Evento</th><th>Equipa</th><th>Destinatário</th><th>Estado</th><th>Assunto</th></tr></thead><tbody>\n<tr><td>07/10/2026 10:58</td><td>Troca revertida</td><td>Equipa Sul</td><td>b@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Troca de turno revertida</td></tr><tr><td>07/10/2026 10:58</td><td>Troca revertida</td><td>Equipa Norte</td><td>a@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Troca de turno revertida</td></tr><tr><td>07/10/2026 10:58</td><td>Troca aprovada</td><td>Equipa Sul</td><td>b@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Troca de turno aprovada</td></tr><tr><td>07/10/2026 10:58</td><td>Troca aprovada</td><td>Equipa Norte</td><td>a@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Troca de turno aprovada</td></tr><tr><td>07/10/2026 10:58</td><td>Troca aceite</td><td>Equipa Norte</td><td>a@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>A sua troca de turno foi aceite</td></tr><tr><td>07/10/2026 10:58</td><td>Troca aguarda aprovação</td><td>Administrador de teste</td><td>admin@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Uma troca de turno precisa de aprovação</td></tr><tr><td>07/10/2026 10:58</td><td>Pedido de troca</td><td>Equipa Sul</td><td>b@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Troca de turnos: sáb 05 jan por sáb 12 jan – Rotação Phase 4</td></tr>\n</tbody></table></div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/notifications",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/admin/schedules"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/month",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/admin/schedules/4?year=2030&month=1"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/schedules/4/apply-rotation",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/admin/schedules/4?year=2030&month=1"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/schedules/4/clear-from",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/admin/assign?on=2030-01-26"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/assign",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/admin/teams"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/teams",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/admin/teams"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/teams/5/update",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Remover Equipa editada · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n \n \n <a href=\"/admin/schedules\">Gerir escalas</a>\n <a href=\"/admin/teams\">Equipas</a>\n <a href=\"/admin/users\">Utilizadores</a>\n <a href=\"/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n <div class=\"flash info\" role=\"status\">Equipa atualizada.</div>\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/admin/teams\">Equipas</a></p>\n <h1>Remover equipa</h1>\n <p class=\"muted\">Isto é definitivo e não pode ser desfeito.</p>\n </div>\n</div>\n<section class=\"panel\">\n <h2>Equipa editada</h2>\n <p>Sem utilizador, apenas aparece na escala.</p>\n <p class=\"muted\">Só é possível remover quem ainda não tem turnos atribuídos. Primeiro transfira os turnos desta equipa para outras; assim o registo de quem trabalhou cada data mantém-se.</p>\n <p class=\"muted\">Ao remover, acontece o seguinte:</p>\n <ul class=\"link-list\">\n <li>0 posição(ões) saem das rotações.</li>\n \n \n <li>O histórico de e-mails é mantido, sem ligação a esta equipa.</li>\n </ul>\n <p class=\"muted\">Se só quiser que esta equipa deixe de receber turnos, desative-a em vez de a remover.</p>\n <div class=\"actions\">\n <form method=\"post\" action=\"/admin/teams/5/delete\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"danger\" type=\"submit\">Remover definitivamente</button>\n </form>\n <a class=\"button secondary\" href=\"/admin/teams\">Cancelar</a>\n </div>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/teams/5/delete",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/admin/teams"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/teams/5/delete",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/admin/users"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/users",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/admin/users"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/users/4/update",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/admin/users"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/users/4/delete",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/"
|
||
},
|
||
"method": "POST",
|
||
"path": "/account",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>A minha conta · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n <a href=\"/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n <div class=\"flash error\" role=\"status\">A nova palavra-passe é igual à atual.</div>\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/\">Rota</a></p>\n <h1>A minha conta</h1>\n <p class=\"muted\">Membro a · @member-a</p>\n \n <p class=\"muted\">Este utilizador entra em nome de 2 equipa(s): Equipa Norte, Equipa partilhada. Vê e controla os turnos de todas.</p>\n \n </div>\n</div>\n\n<section class=\"panel\">\n <h2>Alterar palavra-passe</h2>\n <form method=\"post\" action=\"/account\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"password\" name=\"new_password\" minlength=\"4\" autocomplete=\"new-password\" required autofocus>\n <div><button class=\"primary\" type=\"submit\">Alterar palavra-passe</button></div>\n </form>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/account",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/"
|
||
},
|
||
"method": "POST",
|
||
"path": "/account",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>A minha conta · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/swaps\">Trocas</a>\n <a href=\"/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n <div class=\"flash info\" role=\"status\">Palavra-passe alterada.</div>\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/\">Rota</a></p>\n <h1>A minha conta</h1>\n <p class=\"muted\">Membro a · @member-a</p>\n \n <p class=\"muted\">Este utilizador entra em nome de 2 equipa(s): Equipa Norte, Equipa partilhada. Vê e controla os turnos de todas.</p>\n \n </div>\n</div>\n\n<section class=\"panel\">\n <h2>Alterar palavra-passe</h2>\n <form method=\"post\" action=\"/account\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"password\" name=\"new_password\" minlength=\"4\" autocomplete=\"new-password\" required autofocus>\n <div><button class=\"primary\" type=\"submit\">Alterar palavra-passe</button></div>\n </form>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/account",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/login"
|
||
},
|
||
"method": "POST",
|
||
"path": "/logout",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/account",
|
||
"status": 401
|
||
}
|
||
],
|
||
"rows": {
|
||
"alembic_version": [
|
||
{
|
||
"version_num": "0001_initial"
|
||
}
|
||
],
|
||
"assignments": [
|
||
{
|
||
"date": "2030-01-05",
|
||
"id": 1,
|
||
"note": "Revertida a troca #1 por Administrador de teste",
|
||
"schedule_id": 4,
|
||
"source": "manual",
|
||
"team_id": 2,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-12",
|
||
"id": 2,
|
||
"note": "Revertida a troca #1 por Administrador de teste",
|
||
"schedule_id": 4,
|
||
"source": "manual",
|
||
"team_id": 3,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-19",
|
||
"id": 3,
|
||
"note": null,
|
||
"schedule_id": 4,
|
||
"source": "swap",
|
||
"team_id": 2,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-26",
|
||
"id": 4,
|
||
"note": null,
|
||
"schedule_id": 4,
|
||
"source": "manual",
|
||
"team_id": 3,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-01",
|
||
"id": 5,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-02",
|
||
"id": 6,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-03",
|
||
"id": 7,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-04",
|
||
"id": 8,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-05",
|
||
"id": 9,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-06",
|
||
"id": 10,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-07",
|
||
"id": 11,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-08",
|
||
"id": 12,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-09",
|
||
"id": 13,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-10",
|
||
"id": 14,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-11",
|
||
"id": 15,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-12",
|
||
"id": 16,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-13",
|
||
"id": 17,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-14",
|
||
"id": 18,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-15",
|
||
"id": 19,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-16",
|
||
"id": 20,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-17",
|
||
"id": 21,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-18",
|
||
"id": 22,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-19",
|
||
"id": 23,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-20",
|
||
"id": 24,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-21",
|
||
"id": 25,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-22",
|
||
"id": 26,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-23",
|
||
"id": 27,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-24",
|
||
"id": 28,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-25",
|
||
"id": 29,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-26",
|
||
"id": 30,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-27",
|
||
"id": 31,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-28",
|
||
"id": 32,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-29",
|
||
"id": 33,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-30",
|
||
"id": 34,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-31",
|
||
"id": 35,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-05",
|
||
"id": 36,
|
||
"note": null,
|
||
"schedule_id": 2,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-12",
|
||
"id": 37,
|
||
"note": null,
|
||
"schedule_id": 2,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-19",
|
||
"id": 38,
|
||
"note": null,
|
||
"schedule_id": 2,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-26",
|
||
"id": 39,
|
||
"note": null,
|
||
"schedule_id": 2,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-06",
|
||
"id": 40,
|
||
"note": null,
|
||
"schedule_id": 3,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-13",
|
||
"id": 41,
|
||
"note": null,
|
||
"schedule_id": 3,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-20",
|
||
"id": 42,
|
||
"note": null,
|
||
"schedule_id": 3,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-27",
|
||
"id": 43,
|
||
"note": null,
|
||
"schedule_id": 3,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
}
|
||
],
|
||
"monthly_patterns": [],
|
||
"notification_logs": [
|
||
{
|
||
"body": "Equipa Norte propõe trocar o turno de Rotação Phase 4 de 5 de janeiro de 2030 pelo seu turno de 12 de janeiro de 2030.\n\nTeste de troca\n\nAbrir a escala: http://testserver/swaps",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_requested",
|
||
"id": 1,
|
||
"recipient": "b@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Troca de turnos: sáb 05 jan por sáb 12 jan – Rotação Phase 4",
|
||
"team_id": 3
|
||
},
|
||
{
|
||
"body": "Equipa Sul aceitou a troca de Rotação Phase 4 de 5 de janeiro de 2030 por 12 de janeiro de 2030, pedida por Equipa Norte. Aprove em http://testserver/swaps",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_needs_approval",
|
||
"id": 2,
|
||
"recipient": "admin@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Uma troca de turno precisa de aprovação",
|
||
"team_id": 1
|
||
},
|
||
{
|
||
"body": "Equipa Sul aceitou o seu pedido. Está agora à espera da aprovação da gestão.",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_accepted",
|
||
"id": 3,
|
||
"recipient": "a@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "A sua troca de turno foi aceite",
|
||
"team_id": 2
|
||
},
|
||
{
|
||
"body": "Administrador de teste aprovou a troca de Rotação Phase 4: Equipa Sul fica com o turno de 5 de janeiro de 2030 e Equipa Norte com o de 12 de janeiro de 2030.",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_approved",
|
||
"id": 4,
|
||
"recipient": "a@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Troca de turno aprovada",
|
||
"team_id": 2
|
||
},
|
||
{
|
||
"body": "Administrador de teste aprovou a troca de Rotação Phase 4: Equipa Sul fica com o turno de 5 de janeiro de 2030 e Equipa Norte com o de 12 de janeiro de 2030.",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_approved",
|
||
"id": 5,
|
||
"recipient": "b@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Troca de turno aprovada",
|
||
"team_id": 3
|
||
},
|
||
{
|
||
"body": "Administrador de teste reverteu a troca de Rotação Phase 4. Equipa Norte volta a 5 de janeiro de 2030 e Equipa Sul volta a 12 de janeiro de 2030.\n\nVer a escala: http://testserver/",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_reverted",
|
||
"id": 6,
|
||
"recipient": "a@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Troca de turno revertida",
|
||
"team_id": 2
|
||
},
|
||
{
|
||
"body": "Administrador de teste reverteu a troca de Rotação Phase 4. Equipa Norte volta a 5 de janeiro de 2030 e Equipa Sul volta a 12 de janeiro de 2030.\n\nVer a escala: http://testserver/",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_reverted",
|
||
"id": 7,
|
||
"recipient": "b@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Troca de turno revertida",
|
||
"team_id": 3
|
||
},
|
||
{
|
||
"body": "Já não está atribuído ao turno de Rotação Phase 4 a 26 de janeiro de 2030.\n\nVer a escala: http://testserver/",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "assignment_changed",
|
||
"id": 8,
|
||
"recipient": "b@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Escala atualizada: sáb 26 jan – Rotação Phase 4",
|
||
"team_id": 3
|
||
},
|
||
{
|
||
"body": "Está atribuído ao turno de Rotação Phase 4 a 26 de janeiro de 2030.\n\nVer a escala: http://testserver/",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "assignment_updated",
|
||
"id": 9,
|
||
"recipient": "b@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Escala atualizada: sáb 26 jan – Rotação Phase 4",
|
||
"team_id": 3
|
||
}
|
||
],
|
||
"rotation_members": [
|
||
{
|
||
"id": 1,
|
||
"position": 1,
|
||
"schedule_id": 4,
|
||
"team_id": 2
|
||
},
|
||
{
|
||
"id": 2,
|
||
"position": 2,
|
||
"schedule_id": 4,
|
||
"team_id": 3
|
||
}
|
||
],
|
||
"schedules": [
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"end_time": "00:00:00.000000",
|
||
"id": 1,
|
||
"is_active": 1,
|
||
"name": "Noite",
|
||
"requires_manager_approval": 1,
|
||
"rotation_anchor_date": null,
|
||
"schedule_type": "fixed",
|
||
"slug": "night",
|
||
"start_time": "20:30:00.000000",
|
||
"weekdays": "0,1,2,3,4,5,6"
|
||
},
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"end_time": "16:00:00.000000",
|
||
"id": 2,
|
||
"is_active": 1,
|
||
"name": "Almoço - Sábados",
|
||
"requires_manager_approval": 1,
|
||
"rotation_anchor_date": "2024-01-06",
|
||
"schedule_type": "rotation",
|
||
"slug": "saturday-lunch",
|
||
"start_time": "13:00:00.000000",
|
||
"weekdays": "5"
|
||
},
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"end_time": "16:00:00.000000",
|
||
"id": 3,
|
||
"is_active": 1,
|
||
"name": "Almoço - Domingo",
|
||
"requires_manager_approval": 1,
|
||
"rotation_anchor_date": "2024-01-07",
|
||
"schedule_type": "rotation",
|
||
"slug": "sunday-lunch",
|
||
"start_time": "13:00:00.000000",
|
||
"weekdays": "6"
|
||
},
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"end_time": "14:00:00.000000",
|
||
"id": 4,
|
||
"is_active": 1,
|
||
"name": "Rotação Phase 4",
|
||
"requires_manager_approval": 1,
|
||
"rotation_anchor_date": "2030-01-05",
|
||
"schedule_type": "rotation",
|
||
"slug": "phase4-rotation",
|
||
"start_time": "12:00:00.000000",
|
||
"weekdays": "5"
|
||
}
|
||
],
|
||
"swap_requests": [
|
||
{
|
||
"accepted_team_id": 3,
|
||
"assignment_id": 1,
|
||
"created_at": "<timestamp>",
|
||
"id": 1,
|
||
"message": "Teste de troca",
|
||
"requester_id": 2,
|
||
"status": "reverted",
|
||
"target_assignment_id": 2,
|
||
"target_team_id": 3,
|
||
"updated_at": "<timestamp>"
|
||
}
|
||
],
|
||
"teams": [
|
||
{
|
||
"calendar_token": null,
|
||
"created_at": "<timestamp>",
|
||
"email": "admin@example.com",
|
||
"id": 1,
|
||
"is_active": 1,
|
||
"name": "Administrador de teste",
|
||
"notify_email": 1,
|
||
"phone": null,
|
||
"user_id": 1
|
||
},
|
||
{
|
||
"calendar_token": "phase4-feed-a",
|
||
"created_at": "<timestamp>",
|
||
"email": "a@example.com",
|
||
"id": 2,
|
||
"is_active": 1,
|
||
"name": "Equipa Norte",
|
||
"notify_email": 1,
|
||
"phone": null,
|
||
"user_id": 2
|
||
},
|
||
{
|
||
"calendar_token": "phase4-feed-b",
|
||
"created_at": "<timestamp>",
|
||
"email": "b@example.com",
|
||
"id": 3,
|
||
"is_active": 1,
|
||
"name": "Equipa Sul",
|
||
"notify_email": 1,
|
||
"phone": null,
|
||
"user_id": 3
|
||
},
|
||
{
|
||
"calendar_token": "phase4-feed-c",
|
||
"created_at": "<timestamp>",
|
||
"email": "c@example.com",
|
||
"id": 4,
|
||
"is_active": 1,
|
||
"name": "Equipa partilhada",
|
||
"notify_email": 1,
|
||
"phone": null,
|
||
"user_id": 2
|
||
}
|
||
],
|
||
"users": [
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"id": 1,
|
||
"is_active": 1,
|
||
"is_admin": 1,
|
||
"name": "Administrador de teste",
|
||
"password_hash": "<password-hash>",
|
||
"username": "phase4-admin"
|
||
},
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"id": 2,
|
||
"is_active": 1,
|
||
"is_admin": 0,
|
||
"name": "Membro a",
|
||
"password_hash": "<password-hash>",
|
||
"username": "member-a"
|
||
},
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"id": 3,
|
||
"is_active": 1,
|
||
"is_admin": 0,
|
||
"name": "Membro b",
|
||
"password_hash": "<password-hash>",
|
||
"username": "member-b"
|
||
}
|
||
]
|
||
}
|
||
},
|
||
"/crr": {
|
||
"requests": [
|
||
{
|
||
"body": "{\"status\":\"ok\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/health",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Entrar · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n </nav>\n \n <div class=\"account\"><a href=\"/crr/login\">Entrar</a></div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<section class=\"auth-card\">\n <div class=\"auth-brand\" aria-hidden=\"true\">\n <img src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"\">\n </div>\n <h1>Entrar</h1>\n <p class=\"muted\">Use o seu utilizador e a sua palavra-passe da escala.</p>\n <form method=\"post\" action=\"/crr/login\" class=\"stack\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <label>Utilizador\n <input name=\"username\" autocomplete=\"username\" required autofocus>\n </label>\n <label>Palavra-passe\n <input type=\"password\" name=\"password\" autocomplete=\"current-password\" required>\n </label>\n <button class=\"primary\" type=\"submit\">Entrar</button>\n </form>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/login",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/account",
|
||
"status": 401
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/teams",
|
||
"status": 401
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/users",
|
||
"status": 401
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/schedules",
|
||
"status": 401
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/swaps",
|
||
"status": 401
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"Token CSRF inválido\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "POST",
|
||
"path": "/login",
|
||
"status": 400
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/"
|
||
},
|
||
"method": "POST",
|
||
"path": "/login",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/"
|
||
},
|
||
"method": "POST",
|
||
"path": "/login",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/"
|
||
},
|
||
"method": "POST",
|
||
"path": "/login",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>A minha conta · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n <a href=\"/crr/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n <div class=\"flash info\" role=\"status\">Bem-vindo(a), Equipa Norte, Equipa partilhada.</div>\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/crr/\">Rota</a></p>\n <h1>A minha conta</h1>\n <p class=\"muted\">Membro a · @member-a</p>\n \n <p class=\"muted\">Este utilizador entra em nome de 2 equipa(s): Equipa Norte, Equipa partilhada. Vê e controla os turnos de todas.</p>\n \n </div>\n</div>\n\n<section class=\"panel\">\n <h2>Alterar palavra-passe</h2>\n <form method=\"post\" action=\"/crr/account\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"password\" name=\"new_password\" minlength=\"4\" autocomplete=\"new-password\" required autofocus>\n <div><button class=\"primary\" type=\"submit\">Alterar palavra-passe</button></div>\n </form>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/account",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Calendário · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n <a href=\"/crr/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/crr/\">Rota</a></p>\n <h1>Calendário</h1>\n <p class=\"muted\">Os teus turnos na app de calendário do telemóvel. Subscreve uma vez: as alterações à rota aparecem automaticamente, quando a app atualizar o calendário.</p>\n </div>\n</div>\n\n<div class=\"stack gap-lg\">\n\n <section class=\"panel calendar-feed\">\n <h2>Turnos de Equipa Norte</h2>\n <div class=\"calendar-subscribe-actions\" aria-label=\"Adicionar Turnos de Equipa Norte\">\n <a class=\"button primary\" href=\"webcal://testserver/crr/calendar/phase4-feed-a.ics\">iPhone / Apple Calendar</a>\n <a class=\"button secondary\" href=\"https://calendar.google.com/calendar/render?cid=http%3A%2F%2Ftestserver%2Fcrr%2Fcalendar%2Fphase4-feed-a.ics\" target=\"_blank\" rel=\"noopener noreferrer\">Google Calendar / Android</a>\n <a class=\"button secondary\" href=\"https://outlook.live.com/calendar/0/addfromweb?url=http%3A%2F%2Ftestserver%2Fcrr%2Fcalendar%2Fphase4-feed-a.ics&name=Turnos+de+Equipa+Norte\" target=\"_blank\" rel=\"noopener noreferrer\">Outlook</a>\n </div>\n <p class=\"muted\">No iPhone, confirma a subscrição na app Calendário. No Google e Outlook, adiciona pelo navegador à conta que usas no telemóvel. <a href=\"#calendar-help\">Ver os passos</a>.</p>\n <label for=\"calendar-url-1\">Endereço para subscrever noutra app</label>\n <div class=\"calendar-url-row\">\n <input id=\"calendar-url-1\" type=\"text\" readonly value=\"http://testserver/crr/calendar/phase4-feed-a.ics\" inputmode=\"url\" autocapitalize=\"off\" spellcheck=\"false\">\n <button class=\"secondary\" type=\"button\" data-copy=\"http://testserver/crr/calendar/phase4-feed-a.ics\">Copiar endereço</button>\n </div>\n <p class=\"calendar-copy-status muted\" role=\"status\" data-copy-status></p>\n </section>\n\n <section class=\"panel calendar-feed\">\n <h2>Turnos de Equipa partilhada</h2>\n <div class=\"calendar-subscribe-actions\" aria-label=\"Adicionar Turnos de Equipa partilhada\">\n <a class=\"button primary\" href=\"webcal://testserver/crr/calendar/phase4-feed-c.ics\">iPhone / Apple Calendar</a>\n <a class=\"button secondary\" href=\"https://calendar.google.com/calendar/render?cid=http%3A%2F%2Ftestserver%2Fcrr%2Fcalendar%2Fphase4-feed-c.ics\" target=\"_blank\" rel=\"noopener noreferrer\">Google Calendar / Android</a>\n <a class=\"button secondary\" href=\"https://outlook.live.com/calendar/0/addfromweb?url=http%3A%2F%2Ftestserver%2Fcrr%2Fcalendar%2Fphase4-feed-c.ics&name=Turnos+de+Equipa+partilhada\" target=\"_blank\" rel=\"noopener noreferrer\">Outlook</a>\n </div>\n <p class=\"muted\">No iPhone, confirma a subscrição na app Calendário. No Google e Outlook, adiciona pelo navegador à conta que usas no telemóvel. <a href=\"#calendar-help\">Ver os passos</a>.</p>\n <label for=\"calendar-url-2\">Endereço para subscrever noutra app</label>\n <div class=\"calendar-url-row\">\n <input id=\"calendar-url-2\" type=\"text\" readonly value=\"http://testserver/crr/calendar/phase4-feed-c.ics\" inputmode=\"url\" autocapitalize=\"off\" spellcheck=\"false\">\n <button class=\"secondary\" type=\"button\" data-copy=\"http://testserver/crr/calendar/phase4-feed-c.ics\">Copiar endereço</button>\n </div>\n <p class=\"calendar-copy-status muted\" role=\"status\" data-copy-status></p>\n </section>\n\n</div>\n\n<section class=\"panel calendar-help top-space\" id=\"calendar-help\">\n <h2>Como adicionar no telemóvel</h2>\n <details>\n <summary>iPhone / iPad — Apple Calendar</summary>\n <ol>\n <li>Toca em <strong>iPhone / Apple Calendar</strong> no calendário que queres adicionar.</li>\n <li>Permite abrir a app Calendário e confirma <strong>Subscrever</strong>.</li>\n <li>Se o botão não abrir a app, copia o endereço. Na app Calendário, abre <strong>Calendários → Adicionar calendário → Adicionar calendário de assinatura</strong> e cola-o.</li>\n </ol>\n </details>\n <details>\n <summary>Android / Google Calendar</summary>\n <ol>\n <li>Toca em <strong>Google Calendar / Android</strong> e entra na conta Google que usas no telemóvel.</li>\n <li>O Google exige a versão para computador para adicionar subscrições. No navegador do telemóvel, ativa <strong>Site para computador</strong>; se não conseguires, abre esta página num computador.</li>\n <li>Confirma a adição. Se não aparecer essa opção, copia o endereço e, no Google Calendar, escolhe <strong>Outros calendários → + → A partir de URL</strong>, cola-o e adiciona o calendário.</li>\n <li>Na app Google Calendar, abre o menu e ativa o novo calendário. Se não aparecer, verifica as definições de sincronização.</li>\n </ol>\n </details>\n <details>\n <summary>Outlook</summary>\n <ol>\n <li>Toca em <strong>Outlook</strong> e entra na conta que usas na app.</li>\n <li>Confirma a subscrição no Outlook na Web. Se necessário, usa <strong>Adicionar calendário → Subscrever a partir da Web</strong> e cola o endereço copiado.</li>\n <li>Na app Outlook, seleciona o novo calendário nessa conta. Se a versão móvel não mostrar a opção de subscrever, usa o navegador em modo computador.</li>\n </ol>\n </details>\n <p class=\"muted\">Escolhe uma subscrição por endereço, em vez de importar um ficheiro .ics, para receber futuras alterações. A atualização depende da app e pode demorar algumas horas. Para Google e Outlook, o endereço tem de estar acessível pela Internet.</p>\n</section>\n\n<script>\n // Clipboard permissions can fail even on HTTPS; keep a mobile-friendly fallback.\n document.querySelectorAll(\"[data-copy]\").forEach(function (button) {\n button.addEventListener(\"click\", async function () {\n var url = button.dataset.copy;\n var field = button.parentElement.querySelector(\"input\");\n var status = button.closest(\".calendar-feed\").querySelector(\"[data-copy-status]\");\n var copied = false;\n if (navigator.clipboard && window.isSecureContext) {\n try {\n await navigator.clipboard.writeText(url);\n copied = true;\n } catch (error) {\n // Fall through to selection/copy when clipboard access is denied.\n }\n }\n if (!copied) {\n field.focus();\n field.select();\n field.setSelectionRange(0, field.value.length);\n try {\n copied = document.execCommand(\"copy\");\n } catch (error) {\n // The selected field still supports the phone's native Copy action.\n }\n }\n if (copied) {\n button.textContent = \"Copiado\";\n status.textContent = \"Endereço copiado. Cola-o na tua app de calendário.\";\n setTimeout(function () { button.textContent = \"Copiar endereço\"; }, 2000);\n } else {\n status.textContent = \"Endereço selecionado. Toque no campo e escolha «Copiar».\";\n }\n });\n });\n</script>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/calendar",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n <a href=\"/crr/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div><h1>Pedidos de troca</h1></div>\n</div>\n\n<div class=\"stack gap-lg\">\n\n <div class=\"empty\">Ainda não há pedidos de troca.</div>\n\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/swaps",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Pedir troca · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n <a href=\"/crr/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/crr/swaps\">Trocas</a></p>\n <h1>Pedir troca</h1>\n <p class=\"muted\">Uma troca é entre duas equipas da mesma escala: cada uma fica com o turno da outra.</p>\n </div>\n <div class=\"month-nav\">\n <a class=\"button secondary\" href=\"/crr/swaps/new?year=2029&month=12\">← Anterior</a>\n <a class=\"button secondary\" href=\"/crr/swaps/new?year=2030&month=2\">Seguinte →</a>\n </div>\n</div>\n\n<div class=\"stack gap-lg\">\n\n <section class=\"panel\">\n <div class=\"row between wrap gap\">\n <h2>Rotação Phase 4</h2>\n <span class=\"muted\">Janeiro 2030</span>\n </div>\n <form method=\"post\" action=\"/crr/swaps/new\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <label>O turno que passo\n <select name=\"assignment_id\" required>\n <option value=\"1\">sáb 05 jan · Equipa Norte</option><option value=\"3\">sáb 19 jan · Equipa Norte</option>\n </select>\n </label>\n <label>O turno que quero em troca\n <select name=\"target_assignment_id\" required>\n <option value=\"2\">sáb 12 jan · Equipa Sul</option><option value=\"4\">sáb 26 jan · Equipa Sul</option>\n </select>\n </label>\n <label>Mensagem <span class=\"muted\">(opcional)</span>\n <input name=\"message\" maxlength=\"500\" placeholder=\"Não consigo fazer este dia…\">\n </label>\n <div class=\"row end\"><button class=\"primary\" type=\"submit\">Enviar pedido</button></div>\n </form>\n </section>\n\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/swaps/new?year=2030&month=1",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n <a href=\"/crr/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <h1>Janeiro 2030</h1>\n </div>\n <div class=\"month-nav\">\n \n <a class=\"button secondary\" href=\"/crr/calendar\">Calendário</a>\n <a class=\"button primary\" href=\"/crr/swaps/new?year=2030&month=1\">Pedir troca</a>\n \n <a class=\"button secondary\" href=\"/crr/?year=2029&month=12\">← Anterior</a>\n <a class=\"button secondary\" href=\"/crr/?year=2030&month=2\">Seguinte →</a>\n </div>\n</div>\n\n<div class=\"calendar-list\">\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Ter</strong>\n <span>1</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qua</strong>\n <span>2</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qui</strong>\n <span>3</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sex</strong>\n <span>4</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sáb</strong>\n <span>5</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Sábados</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment mine\">\n <div>\n <div class=\"assignment-title\">Rotação Phase 4</div>\n <div class=\"assignment-team\">\n Equipa Norte\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Dom</strong>\n <span>6</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Domingo</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Seg</strong>\n <span>7</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Ter</strong>\n <span>8</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qua</strong>\n <span>9</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qui</strong>\n <span>10</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sex</strong>\n <span>11</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sáb</strong>\n <span>12</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Sábados</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Rotação Phase 4</div>\n <div class=\"assignment-team\">\n Equipa Sul\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Dom</strong>\n <span>13</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Domingo</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Seg</strong>\n <span>14</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Ter</strong>\n <span>15</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qua</strong>\n <span>16</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qui</strong>\n <span>17</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sex</strong>\n <span>18</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sáb</strong>\n <span>19</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Sábados</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment mine\">\n <div>\n <div class=\"assignment-title\">Rotação Phase 4</div>\n <div class=\"assignment-team\">\n Equipa Norte\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Dom</strong>\n <span>20</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Domingo</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Seg</strong>\n <span>21</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Ter</strong>\n <span>22</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qua</strong>\n <span>23</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qui</strong>\n <span>24</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sex</strong>\n <span>25</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Sáb</strong>\n <span>26</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Sábados</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Rotação Phase 4</div>\n <div class=\"assignment-team\">\n Equipa Sul\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Dom</strong>\n <span>27</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Almoço - Domingo</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Seg</strong>\n <span>28</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Ter</strong>\n <span>29</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qua</strong>\n <span>30</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n <section class=\"day-card \">\n <div class=\"day-label\">\n <strong>Qui</strong>\n <span>31</span>\n </div>\n <div class=\"day-assignments\">\n \n <article class=\"assignment \">\n <div>\n <div class=\"assignment-title\">Noite</div>\n <div class=\"assignment-team\">\n <span class=\"unassigned\">Sem atribuição</span>\n </div>\n </div>\n \n \n </article>\n \n </div>\n </section>\n\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/?year=2030&month=1",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Exportar escala · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n <a href=\"/crr/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/crr/\">Rota</a></p>\n <h1>Exportar escala</h1>\n <p class=\"muted\">Escolhe uma escala e as datas. O PDF é descarregado numa única folha A4, com os dias e as equipas agrupados por mês.</p>\n </div>\n</div>\n<section class=\"panel\">\n \n<form class=\"stack\" method=\"get\" action=\"/crr/export/schedule.pdf\">\n <label>Escala\n <select name=\"schedule_id\" required>\n \n <option value=\"1\" selected>Noite</option>\n \n <option value=\"2\" >Almoço - Sábados</option>\n \n <option value=\"3\" >Almoço - Domingo</option>\n \n <option value=\"4\" >Rotação Phase 4</option>\n \n </select>\n </label>\n <div class=\"form-grid\">\n <label>Data inicial\n <input type=\"date\" name=\"start_date\" value=\"2026-10-01\" min=\"2000-01-01\" max=\"2100-12-31\" required>\n </label>\n <label>Data final\n <input type=\"date\" name=\"end_date\" value=\"2026-10-31\" min=\"2000-01-01\" max=\"2100-12-31\" required>\n </label>\n </div>\n <p class=\"muted\">As datas inicial e final estão incluídas. Máximo de 12 meses; o tamanho do texto adapta-se ao intervalo.</p>\n <div class=\"row end\"><button class=\"primary\" type=\"submit\">Descarregar PDF</button></div>\n</form>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/export/schedule",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessária a permissão de administrador\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/users",
|
||
"status": 403
|
||
},
|
||
{
|
||
"body": "BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:-//Contínuos CRR//Rota//PT\r\nCALSCALE:GREGORIAN\r\nMETHOD:PUBLISH\r\nX-WR-CALNAME:Contínuos CRR — Equipa Norte\r\nX-WR-CALDESC:Contínuos CRR — 1 de agosto de 2026 a 31 de março de 2027\r\nREFRESH-INTERVAL;VALUE=DURATION:PT12H\r\nX-PUBLISHED-TTL:PT12H\r\nX-WR-TIMEZONE:Europe/Lisbon\r\nBEGIN:VTIMEZONE\r\nTZID:Europe/Lisbon\r\nX-LIC-LOCATION:Europe/Lisbon\r\nBEGIN:DAYLIGHT\r\nTZOFFSETFROM:+0000\r\nTZOFFSETTO:+0100\r\nTZNAME:WEST\r\nDTSTART:19700329T020000\r\nRRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU\r\nEND:DAYLIGHT\r\nBEGIN:STANDARD\r\nTZOFFSETFROM:+0100\r\nTZOFFSETTO:+0000\r\nTZNAME:WET\r\nDTSTART:19701025T030000\r\nRRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU\r\nEND:STANDARD\r\nEND:VTIMEZONE\r\nEND:VCALENDAR\r\n",
|
||
"headers": {
|
||
"cache-control": "private, max-age=300",
|
||
"content-disposition": "inline; filename=\"rota.ics\"",
|
||
"content-type": "text/calendar; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/calendar/phase4-feed-a.ics",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"Calendário não encontrado\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/calendar/not-a-token.ics",
|
||
"status": 404
|
||
},
|
||
{
|
||
"body": "CONTÍNUOS CRR\nRotação Phase 4\nJaneiro 2030\n 5\nEquipa Norte\n12\nEquipa Sul\n19\nEquipa Norte\n26\nEquipa Sul\n",
|
||
"headers": {
|
||
"cache-control": "private, no-store",
|
||
"content-disposition": "attachment; filename=\"escala-4-2030-01-01-2030-01-31.pdf\"",
|
||
"content-type": "application/pdf"
|
||
},
|
||
"method": "GET",
|
||
"path": "/export/schedule.pdf?schedule_id=4&start_date=2030-01-01&end_date=2030-01-31",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/swaps"
|
||
},
|
||
"method": "POST",
|
||
"path": "/assignments/1/swap",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/swaps"
|
||
},
|
||
"method": "POST",
|
||
"path": "/swaps/1/accept",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/swaps"
|
||
},
|
||
"method": "POST",
|
||
"path": "/swaps/1/approve",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/swaps"
|
||
},
|
||
"method": "POST",
|
||
"path": "/swaps/1/revert",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Equipas · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n \n \n <a href=\"/crr/admin/schedules\">Gerir escalas</a>\n <a href=\"/crr/admin/teams\">Equipas</a>\n <a href=\"/crr/admin/users\">Utilizadores</a>\n <a href=\"/crr/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n <div class=\"flash info\" role=\"status\">Troca revertida: os dois turnos voltaram a quem os tinha.</div>\n \n \n<div class=\"page-head\">\n <div>\n <h1>Equipas</h1>\n <p class=\"muted\">Quem entra na rota. Uma equipa é quem fica com um turno, e pode ser uma pessoa ou várias. Os utilizadores que entram na aplicação criam-se em <a href=\"/crr/admin/users\">Utilizadores</a>; aqui escolhe-se apenas quem entra para cada equipa.</p>\n </div>\n</div>\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <h2>Adicionar equipa</h2>\n <button class=\"primary\" type=\"submit\" form=\"add-team\">Adicionar equipa</button>\n </div>\n <form id=\"add-team\" method=\"post\" action=\"/crr/admin/teams\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" required></label>\n <label>Telefone<input name=\"phone\"></label>\n <label>E-mail<input type=\"email\" name=\"email\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Utilizador <span class=\"muted\">(opcional)</span></legend>\n <label>Entra com\n <select name=\"assigned_user_id\">\n <option value=\"\">Ninguém — só aparece na escala</option>\n <option value=\"2\">@member-a — Membro a</option><option value=\"3\">@member-b — Membro b</option><option value=\"1\">@phase4-admin — Administrador de teste</option>\n </select>\n </label>\n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"notify_email\" checked> Notificações por e-mail</label>\n </form>\n</section>\n<div class=\"stack gap-lg top-space\">\n\n<details class=\"panel\">\n <summary><strong>Administrador de teste</strong> <span class=\"muted\">@phase4-admin</span></summary>\n <form method=\"post\" action=\"/crr/admin/teams/1/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Administrador de teste\" required></label>\n <label>Telefone<input name=\"phone\" value=\"\"></label>\n <label>E-mail<input type=\"email\" name=\"email\" value=\"admin@example.com\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Utilizador</legend>\n <label>Entra com\n <select name=\"assigned_user_id\">\n <option value=\"\">Ninguém — só aparece na escala</option>\n <option value=\"2\" >@member-a — Membro a</option><option value=\"3\" >@member-b — Membro b</option><option value=\"1\" selected>@phase4-admin — Administrador de teste</option>\n </select>\n </label>\n <p class=\"muted\">O mesmo utilizador pode entrar em várias equipas: vê e controla os turnos de todas elas.</p>\n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"notify_email\" checked> Notificações por e-mail</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativa</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n</details>\n\n<details class=\"panel\">\n <summary><strong>Equipa Norte</strong> <span class=\"muted\">@member-a · partilhada com 1</span></summary>\n <form method=\"post\" action=\"/crr/admin/teams/2/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Equipa Norte\" required></label>\n <label>Telefone<input name=\"phone\" value=\"\"></label>\n <label>E-mail<input type=\"email\" name=\"email\" value=\"a@example.com\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Utilizador</legend>\n <label>Entra com\n <select name=\"assigned_user_id\">\n <option value=\"\">Ninguém — só aparece na escala</option>\n <option value=\"2\" selected>@member-a — Membro a</option><option value=\"3\" >@member-b — Membro b</option><option value=\"1\" >@phase4-admin — Administrador de teste</option>\n </select>\n </label>\n <p class=\"muted\">O mesmo utilizador pode entrar em várias equipas: vê e controla os turnos de todas elas.</p>\n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"notify_email\" checked> Notificações por e-mail</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativa</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n</details>\n\n<details class=\"panel\">\n <summary><strong>Equipa Sul</strong> <span class=\"muted\">@member-b</span></summary>\n <form method=\"post\" action=\"/crr/admin/teams/3/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Equipa Sul\" required></label>\n <label>Telefone<input name=\"phone\" value=\"\"></label>\n <label>E-mail<input type=\"email\" name=\"email\" value=\"b@example.com\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Utilizador</legend>\n <label>Entra com\n <select name=\"assigned_user_id\">\n <option value=\"\">Ninguém — só aparece na escala</option>\n <option value=\"2\" >@member-a — Membro a</option><option value=\"3\" selected>@member-b — Membro b</option><option value=\"1\" >@phase4-admin — Administrador de teste</option>\n </select>\n </label>\n <p class=\"muted\">O mesmo utilizador pode entrar em várias equipas: vê e controla os turnos de todas elas.</p>\n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"notify_email\" checked> Notificações por e-mail</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativa</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n</details>\n\n<details class=\"panel\">\n <summary><strong>Equipa partilhada</strong> <span class=\"muted\">@member-a · partilhada com 1</span></summary>\n <form method=\"post\" action=\"/crr/admin/teams/4/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Equipa partilhada\" required></label>\n <label>Telefone<input name=\"phone\" value=\"\"></label>\n <label>E-mail<input type=\"email\" name=\"email\" value=\"c@example.com\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Utilizador</legend>\n <label>Entra com\n <select name=\"assigned_user_id\">\n <option value=\"\">Ninguém — só aparece na escala</option>\n <option value=\"2\" selected>@member-a — Membro a</option><option value=\"3\" >@member-b — Membro b</option><option value=\"1\" >@phase4-admin — Administrador de teste</option>\n </select>\n </label>\n <p class=\"muted\">O mesmo utilizador pode entrar em várias equipas: vê e controla os turnos de todas elas.</p>\n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"notify_email\" checked> Notificações por e-mail</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativa</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n <div><a class=\"button danger\" href=\"/crr/admin/teams/4/delete\">Remover</a></div>\n </form>\n</details>\n\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/teams",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Utilizadores · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n \n \n <a href=\"/crr/admin/schedules\">Gerir escalas</a>\n <a href=\"/crr/admin/teams\">Equipas</a>\n <a href=\"/crr/admin/users\">Utilizadores</a>\n <a href=\"/crr/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <h1>Utilizadores</h1>\n <p class=\"muted\">Cada utilizador tem utilizador e palavra-passe e pode entrar em várias equipas. Quem entra vê e controla os turnos de todas elas, e a administração é do utilizador, não da equipa.</p>\n </div>\n</div>\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <h2>Adicionar utilizador</h2>\n <button class=\"primary\" type=\"submit\" form=\"add-user\">Adicionar utilizador</button>\n </div>\n <form id=\"add-user\" method=\"post\" action=\"/crr/admin/users\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome <span class=\"muted\">(ex.: Equipa de sábado)</span><input name=\"name\" required></label>\n <label>Utilizador<input name=\"username\" pattern=\"[A-Za-z0-9._-]+\" required></label>\n <label>Palavra-passe<input type=\"password\" name=\"password\" minlength=\"4\" required></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Equipas <span class=\"muted\">(pode escolher várias)</span></legend>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"1\"> Administrador de teste</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"2\"> Equipa Norte</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"3\"> Equipa Sul</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"4\"> Equipa partilhada</label>\n \n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_admin\"> Administrador</label>\n </form>\n</section>\n<div class=\"stack gap-lg top-space\">\n\n<details class=\"panel\">\n <summary><strong>@member-a</strong> <span class=\"muted\">Membro a · 2 equipa(s)</span></summary>\n <form method=\"post\" action=\"/crr/admin/users/2/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Membro a\" required></label>\n <label>Utilizador<input name=\"username\" pattern=\"[A-Za-z0-9._-]+\" value=\"member-a\" required></label>\n <label>Nova palavra-passe <span class=\"muted\">(vazio mantém a atual)</span><input type=\"password\" name=\"password\" minlength=\"4\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Equipas que entra</legend>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"1\" > Administrador de teste</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"2\" checked> Equipa Norte</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"3\" > Equipa Sul</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"4\" checked> Equipa partilhada</label>\n \n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_admin\" > Administrador</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativo</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n \n <p class=\"muted\">Equipas: Equipa Norte, Equipa partilhada</p>\n \n</details>\n\n<details class=\"panel\">\n <summary><strong>@member-b</strong> <span class=\"muted\">Membro b · 1 equipa(s)</span></summary>\n <form method=\"post\" action=\"/crr/admin/users/3/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Membro b\" required></label>\n <label>Utilizador<input name=\"username\" pattern=\"[A-Za-z0-9._-]+\" value=\"member-b\" required></label>\n <label>Nova palavra-passe <span class=\"muted\">(vazio mantém a atual)</span><input type=\"password\" name=\"password\" minlength=\"4\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Equipas que entra</legend>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"1\" > Administrador de teste</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"2\" > Equipa Norte</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"3\" checked> Equipa Sul</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"4\" > Equipa partilhada</label>\n \n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_admin\" > Administrador</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativo</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n \n <p class=\"muted\">Equipas: Equipa Sul</p>\n \n</details>\n\n<details class=\"panel\">\n <summary><strong>@phase4-admin</strong> <span class=\"muted\">Administrador de teste · administrador · 1 equipa(s)</span></summary>\n <form method=\"post\" action=\"/crr/admin/users/1/update\" class=\"form-grid top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <div class=\"form-col\">\n <label>Nome<input name=\"name\" value=\"Administrador de teste\" required></label>\n <label>Utilizador<input name=\"username\" pattern=\"[A-Za-z0-9._-]+\" value=\"phase4-admin\" required></label>\n <label>Nova palavra-passe <span class=\"muted\">(vazio mantém a atual)</span><input type=\"password\" name=\"password\" minlength=\"4\"></label>\n </div>\n <fieldset class=\"form-col\">\n <legend>Equipas que entra</legend>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"1\" checked> Administrador de teste</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"2\" > Equipa Norte</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"3\" > Equipa Sul</label>\n \n <label class=\"check\"><input type=\"checkbox\" name=\"team_ids\" value=\"4\" > Equipa partilhada</label>\n \n </fieldset>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_admin\" checked> Administrador</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativo</label>\n <div><button class=\"primary\" type=\"submit\">Guardar</button></div>\n \n </form>\n \n <p class=\"muted\">Equipas: Administrador de teste</p>\n \n</details>\n\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/users",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n \n \n <a href=\"/crr/admin/schedules\">Gerir escalas</a>\n <a href=\"/crr/admin/teams\">Equipas</a>\n <a href=\"/crr/admin/users\">Utilizadores</a>\n <a href=\"/crr/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <h1>Escalas</h1>\n <p class=\"muted\">Uma página por tipo de escala. A escala da noite é fixa e repete todos os meses; o almoço de sábado e o de domingo são rotações independentes.</p>\n </div>\n <a class=\"button primary\" href=\"/crr/admin/assign\">Atribuir por dia</a>\n</div>\n<div class=\"stack gap\">\n\n<section class=\"panel\" id=\"export-pdf\">\n <h2>Exportar PDF</h2>\n <p class=\"muted\">Escolhe uma escala e as datas para descarregar a lista de dias e equipas numa única folha A4.</p>\n \n<form class=\"stack\" method=\"get\" action=\"/crr/export/schedule.pdf\">\n <label>Escala\n <select name=\"schedule_id\" required>\n \n <option value=\"1\" selected>Noite</option>\n \n <option value=\"2\" >Almoço - Sábados</option>\n \n <option value=\"3\" >Almoço - Domingo</option>\n \n <option value=\"4\" >Rotação Phase 4</option>\n \n </select>\n </label>\n <div class=\"form-grid\">\n <label>Data inicial\n <input type=\"date\" name=\"start_date\" value=\"2026-10-01\" min=\"2000-01-01\" max=\"2100-12-31\" required>\n </label>\n <label>Data final\n <input type=\"date\" name=\"end_date\" value=\"2026-10-31\" min=\"2000-01-01\" max=\"2100-12-31\" required>\n </label>\n </div>\n <p class=\"muted\">As datas inicial e final estão incluídas. Máximo de 12 meses; o tamanho do texto adapta-se ao intervalo.</p>\n <div class=\"row end\"><button class=\"primary\" type=\"submit\">Descarregar PDF</button></div>\n</form>\n</section>\n\n\n\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <div>\n <h2>Noite</h2>\n <p class=\"muted\">Todos os dias · repete todos os meses · 20:30–00:00</p>\n </div>\n <div class=\"actions\">\n <a class=\"button secondary\" href=\"/crr/admin/schedules?schedule_id=1#export-pdf\">Exportar PDF</a>\n <a class=\"button primary\" href=\"/crr/admin/schedules/1\">Configurar</a>\n </div>\n </div>\n \n <h3>Padrão</h3>\n <ul class=\"link-list\">\n <li class=\"muted\">Ainda não há ninguém definido.</li>\n </ul>\n \n</section>\n\n\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <div>\n <h2>Almoço - Sábados</h2>\n <p class=\"muted\">Rotação · Sábado · 13:00–16:00</p>\n </div>\n <div class=\"actions\">\n <a class=\"button secondary\" href=\"/crr/admin/schedules?schedule_id=2#export-pdf\">Exportar PDF</a>\n <a class=\"button primary\" href=\"/crr/admin/schedules/2\">Configurar</a>\n </div>\n </div>\n \n <p class=\"muted\">Próximas datas</p>\n <p class=\"date-list\">sáb 10 out · sáb 17 out · sáb 24 out · sáb 31 out · sáb 07 nov · sáb 14 nov</p>\n <h3>Ordem da rotação</h3>\n <ol class=\"rotation-list\">\n <li class=\"muted\">Ainda não há membros.</li>\n </ol>\n \n</section>\n\n\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <div>\n <h2>Almoço - Domingo</h2>\n <p class=\"muted\">Rotação · Domingo · 13:00–16:00</p>\n </div>\n <div class=\"actions\">\n <a class=\"button secondary\" href=\"/crr/admin/schedules?schedule_id=3#export-pdf\">Exportar PDF</a>\n <a class=\"button primary\" href=\"/crr/admin/schedules/3\">Configurar</a>\n </div>\n </div>\n \n <p class=\"muted\">Próximas datas</p>\n <p class=\"date-list\">dom 11 out · dom 18 out · dom 25 out · dom 01 nov · dom 08 nov · dom 15 nov</p>\n <h3>Ordem da rotação</h3>\n <ol class=\"rotation-list\">\n <li class=\"muted\">Ainda não há membros.</li>\n </ol>\n \n</section>\n\n\n<section class=\"panel\">\n <div class=\"row between wrap gap\">\n <div>\n <h2>Rotação Phase 4</h2>\n <p class=\"muted\">Rotação · Sábado · 12:00–14:00</p>\n </div>\n <div class=\"actions\">\n <a class=\"button secondary\" href=\"/crr/admin/schedules?schedule_id=4#export-pdf\">Exportar PDF</a>\n <a class=\"button primary\" href=\"/crr/admin/schedules/4\">Configurar</a>\n </div>\n </div>\n \n <p class=\"muted\">Próximas datas</p>\n <p class=\"date-list\">sáb 10 out · sáb 17 out · sáb 24 out · sáb 31 out · sáb 07 nov · sáb 14 nov</p>\n <h3>Ordem da rotação</h3>\n <ol class=\"rotation-list\">\n \n <li><span>Equipa Norte</span></li>\n \n <li><span>Equipa Sul</span></li>\n \n </ol>\n \n</section>\n\n<section class=\"panel\">\n <h2>Calendário da equipa</h2>\n <p class=\"muted\">Um único endereço com a rota completa, para qualquer calendário.</p>\n <p><a class=\"button secondary\" href=\"/crr/calendar\">Adicionar ao calendário do telemóvel</a></p>\n <div class=\"inline-form\">\n <input type=\"text\" readonly value=\"http://testserver/crr/calendar/phase4-whole-rota.ics\" aria-label=\"Endereço do calendário da equipa\">\n <button class=\"secondary\" type=\"button\" data-copy=\"http://testserver/crr/calendar/phase4-whole-rota.ics\">Copiar</button>\n </div>\n</section>\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/schedules",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n \n \n <a href=\"/crr/admin/schedules\">Gerir escalas</a>\n <a href=\"/crr/admin/teams\">Equipas</a>\n <a href=\"/crr/admin/users\">Utilizadores</a>\n <a href=\"/crr/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n\n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/crr/admin/schedules\">Escalas</a></p>\n <h1>Rotação Phase 4</h1>\n <p class=\"muted\">Rotação · Sábado · 12:00–14:00</p>\n </div>\n <form method=\"post\" action=\"/crr/admin/schedules/4/settings\" class=\"inline-form\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <label>Das horas<input type=\"time\" name=\"start_time\" value=\"12:00\" required></label>\n <label>Às horas<input type=\"time\" name=\"end_time\" value=\"14:00\" required></label>\n <label class=\"check\"><input type=\"checkbox\" name=\"requires_manager_approval\" checked> Aprovação da gestão</label>\n <label class=\"check\"><input type=\"checkbox\" name=\"is_active\" checked> Ativa</label>\n <button class=\"secondary\" type=\"submit\">Guardar</button>\n </form>\n</div>\n<div class=\"stack gap-lg\">\n\n <section class=\"panel\">\n <div class=\"row between wrap gap\">\n <div><h3>Janeiro 2030</h3></div>\n <div class=\"month-nav\">\n <a class=\"button secondary\" href=\"/crr/admin/schedules/4?year=2029&month=12\">← Anterior</a>\n <a class=\"button secondary\" href=\"/crr/admin/schedules/4?year=2030&month=2\">Seguinte →</a>\n </div>\n </div>\n <form method=\"post\" action=\"/crr/admin/schedules/4/apply-rotation\" class=\"inline-form\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"hidden\" name=\"year\" value=\"2030\"><input type=\"hidden\" name=\"month\" value=\"1\">\n <button class=\"secondary\" type=\"submit\">Aplicar rotação em Janeiro</button>\n </form>\n <p class=\"muted\">Deixa sem atribuição os turnos <strong>automáticos</strong> do mês e ano escolhidos e de todos os seguintes. Nenhum turno é apagado e as atribuições manuais e as trocas concluídas são mantidas. Use Limpar turnos antes de Aplicar rotação, para recalcular tudo a partir de um ponto.</p>\n <form method=\"post\" action=\"/crr/admin/schedules/4/assignments\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"hidden\" name=\"year\" value=\"2030\"><input type=\"hidden\" name=\"month\" value=\"1\">\n <div class=\"table-wrap\"><table><thead><tr><th>Data</th><th>Equipa</th><th>Origem</th><th>Ação</th></tr></thead><tbody>\n \n <tr><td>sáb 05 jan</td><td><select name=\"assignment_1\"><option value=\"\">Sem atribuição</option><option value=\"1\" >Administrador de teste</option><option value=\"2\" selected>Equipa Norte</option><option value=\"3\" >Equipa Sul</option><option value=\"4\" >Equipa partilhada</option></select></td><td><span class=\"badge\">manual</span></td><td><button class=\"secondary small\" type=\"submit\" form=\"regenerate-1\" aria-label=\"Recalcular o turno de sáb 05 jan\">Recalcular</button></td></tr>\n \n <tr><td>sáb 12 jan</td><td><select name=\"assignment_2\"><option value=\"\">Sem atribuição</option><option value=\"1\" >Administrador de teste</option><option value=\"2\" >Equipa Norte</option><option value=\"3\" selected>Equipa Sul</option><option value=\"4\" >Equipa partilhada</option></select></td><td><span class=\"badge\">manual</span></td><td><button class=\"secondary small\" type=\"submit\" form=\"regenerate-2\" aria-label=\"Recalcular o turno de sáb 12 jan\">Recalcular</button></td></tr>\n \n <tr><td>sáb 19 jan</td><td><select name=\"assignment_3\"><option value=\"\">Sem atribuição</option><option value=\"1\" >Administrador de teste</option><option value=\"2\" selected>Equipa Norte</option><option value=\"3\" >Equipa Sul</option><option value=\"4\" >Equipa partilhada</option></select></td><td><span class=\"badge\">troca</span></td><td><button class=\"secondary small\" type=\"submit\" form=\"regenerate-3\" aria-label=\"Recalcular o turno de sáb 19 jan\">Recalcular</button></td></tr>\n \n <tr><td>sáb 26 jan</td><td><select name=\"assignment_4\"><option value=\"\">Sem atribuição</option><option value=\"1\" >Administrador de teste</option><option value=\"2\" >Equipa Norte</option><option value=\"3\" selected>Equipa Sul</option><option value=\"4\" >Equipa partilhada</option></select></td><td><span class=\"badge\">automático</span></td><td><button class=\"secondary small\" type=\"submit\" form=\"regenerate-4\" aria-label=\"Recalcular o turno de sáb 26 jan\">Recalcular</button></td></tr>\n \n </tbody></table></div>\n <div class=\"sticky-actions\"><button class=\"primary\" type=\"submit\">Guardar Janeiro</button></div>\n </form>\n \n <form id=\"regenerate-1\" method=\"post\" action=\"/crr/admin/schedules/4/assignments/1/regenerate\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n </form>\n \n <form id=\"regenerate-2\" method=\"post\" action=\"/crr/admin/schedules/4/assignments/2/regenerate\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n </form>\n \n <form id=\"regenerate-3\" method=\"post\" action=\"/crr/admin/schedules/4/assignments/3/regenerate\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n </form>\n \n <form id=\"regenerate-4\" method=\"post\" action=\"/crr/admin/schedules/4/assignments/4/regenerate\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n </form>\n \n </section>\n <section class=\"panel\">\n <h3>Próximas datas</h3>\n <p class=\"date-list\">sáb 10 out · sáb 17 out · sáb 24 out · sáb 31 out · sáb 07 nov · sáb 14 nov</p>\n </section>\n <section class=\"panel\" id=\"rotation-order\">\n <h3>Ordem da rotação</h3>\n <p class=\"muted\">Rotação independente, preenchida por ordem. Depois de alterar a ordem, use Aplicar rotação acima para atualizar os turnos automáticos deste mês.</p>\n <ol class=\"rotation-list\">\n \n <li>\n <span>Equipa Norte</span>\n <div class=\"actions compact\">\n <form method=\"post\" action=\"/crr/admin/rotation/1/move\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><input type=\"hidden\" name=\"direction\" value=\"up\"><button class=\"secondary small\" type=\"submit\" aria-label=\"Subir Equipa Norte\">↑</button></form>\n <form method=\"post\" action=\"/crr/admin/rotation/1/move\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><input type=\"hidden\" name=\"direction\" value=\"down\"><button class=\"secondary small\" type=\"submit\" aria-label=\"Descer Equipa Norte\">↓</button></form>\n <form method=\"post\" action=\"/crr/admin/rotation/1/remove\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><button class=\"danger small\" type=\"submit\">Remover</button></form>\n </div>\n </li>\n \n <li>\n <span>Equipa Sul</span>\n <div class=\"actions compact\">\n <form method=\"post\" action=\"/crr/admin/rotation/2/move\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><input type=\"hidden\" name=\"direction\" value=\"up\"><button class=\"secondary small\" type=\"submit\" aria-label=\"Subir Equipa Sul\">↑</button></form>\n <form method=\"post\" action=\"/crr/admin/rotation/2/move\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><input type=\"hidden\" name=\"direction\" value=\"down\"><button class=\"secondary small\" type=\"submit\" aria-label=\"Descer Equipa Sul\">↓</button></form>\n <form method=\"post\" action=\"/crr/admin/rotation/2/remove\"><input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\"><button class=\"danger small\" type=\"submit\">Remover</button></form>\n </div>\n </li>\n \n </ol>\n <form method=\"post\" action=\"/crr/admin/schedules/4/rotation\" class=\"inline-form\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <select name=\"team_id\" required><option value=\"\">Adicionar equipa…</option><option value=\"1\">Administrador de teste</option><option value=\"2\">Equipa Norte</option><option value=\"3\">Equipa Sul</option><option value=\"4\">Equipa partilhada</option></select>\n <button class=\"primary\" type=\"submit\">Adicionar à rotação</button>\n </form>\n </section>\n\n <section class=\"panel\">\n <h3>Limpar atribuições</h3>\n <p class=\"muted\">Limpar atribuições a partir do mês de:</p>\n <form method=\"post\" action=\"/crr/admin/schedules/4/clear-from\" class=\"inline-form\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <label><select name=\"month\">\n <option value=\"1\" selected>Janeiro</option><option value=\"2\" >Fevereiro</option><option value=\"3\" >Março</option><option value=\"4\" >Abril</option><option value=\"5\" >Maio</option><option value=\"6\" >Junho</option><option value=\"7\" >Julho</option><option value=\"8\" >Agosto</option><option value=\"9\" >Setembro</option><option value=\"10\" >Outubro</option><option value=\"11\" >Novembro</option><option value=\"12\" >Dezembro</option>\n </select></label>\n <label><input type=\"number\" name=\"year\" value=\"2030\" min=\"2000\" max=\"2100\" required></label>\n <button class=\"danger\" type=\"submit\">Limpar turnos</button>\n </form>\n </section>\n <section class=\"panel\" id=\"swap-requests\">\n <h3>Pedidos de troca</h3>\n <p class=\"muted\">Um administrador pode atribuir o turno a outra equipa, mesmo que nenhuma pessoa entre na aplicação por ela. Só se aplica se quem pediu a troca continuar a ser o titular do turno.</p>\n <ul class=\"link-list\">\n \n <li class=\"muted\">Não há pedidos de troca nesta escala.</li>\n \n </ul>\n </section>\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/schedules/4?year=2030&month=1",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Atribuição por dia · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n \n \n <a href=\"/crr/admin/schedules\">Gerir escalas</a>\n <a href=\"/crr/admin/teams\">Equipas</a>\n <a href=\"/crr/admin/users\">Utilizadores</a>\n <a href=\"/crr/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/crr/admin/schedules\">Escalas</a></p>\n <h1>Atribuição por dia</h1>\n <p class=\"muted\">Escolha o dia e a equipa que fica com cada turno. É uma atribuição direta, não uma troca.</p>\n </div>\n <form method=\"get\" action=\"/crr/admin/assign\" class=\"inline-form\">\n <label>Dia<input type=\"date\" name=\"on\" value=\"2030-01-05\" min=\"1999-12-31\" max=\"2100-12-30\" required onchange=\"this.form.submit()\"></label>\n </form>\n</div>\n\n<div class=\"month-nav\">\n <a class=\"button secondary\" href=\"/crr/admin/assign?on=2030-01-04\">← Dia anterior</a>\n <a class=\"button secondary \" href=\"/crr/admin/assign?on=2026-10-07\">Hoje</a>\n <a class=\"button secondary\" href=\"/crr/admin/assign?on=2030-01-06\">Dia seguinte →</a>\n</div>\n\n<div class=\"stack gap-lg top-space\">\n <section class=\"panel\">\n <h2>Sábado, 5 de janeiro de 2030</h2>\n \n <form method=\"post\" action=\"/crr/admin/assign\" class=\"row between wrap gap top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"hidden\" name=\"assignment_id\" value=\"9\">\n <div>\n <strong>Noite</strong>\n <span class=\"muted\"> · 20:30–00:00</span>\n <div class=\"muted\">\n \n Agora: sem atribuição\n \n </div>\n </div>\n <div class=\"inline-form\">\n <select name=\"team_id\">\n <option value=\"auto\" selected>Automático → sem ninguém</option>\n <option value=\"\" >Sem atribuição</option>\n \n <option value=\"1\" >Administrador de teste</option>\n \n <option value=\"2\" >Equipa Norte</option>\n \n <option value=\"3\" >Equipa Sul</option>\n \n <option value=\"4\" >Equipa partilhada</option>\n \n </select>\n <button class=\"secondary small\" type=\"submit\">Atribuir</button>\n </div>\n </form>\n \n <form method=\"post\" action=\"/crr/admin/assign\" class=\"row between wrap gap top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"hidden\" name=\"assignment_id\" value=\"36\">\n <div>\n <strong>Almoço - Sábados</strong>\n <span class=\"muted\"> · 13:00–16:00</span>\n <div class=\"muted\">\n \n Agora: sem atribuição\n \n </div>\n </div>\n <div class=\"inline-form\">\n <select name=\"team_id\">\n <option value=\"auto\" selected>Automático → sem ninguém</option>\n <option value=\"\" >Sem atribuição</option>\n \n <option value=\"1\" >Administrador de teste</option>\n \n <option value=\"2\" >Equipa Norte</option>\n \n <option value=\"3\" >Equipa Sul</option>\n \n <option value=\"4\" >Equipa partilhada</option>\n \n </select>\n <button class=\"secondary small\" type=\"submit\">Atribuir</button>\n </div>\n </form>\n \n <form method=\"post\" action=\"/crr/admin/assign\" class=\"row between wrap gap top-space\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"hidden\" name=\"assignment_id\" value=\"1\">\n <div>\n <strong>Rotação Phase 4</strong>\n <span class=\"muted\"> · 12:00–14:00</span>\n <div class=\"muted\">\n \n Agora: Equipa Norte (manual)\n \n </div>\n </div>\n <div class=\"inline-form\">\n <select name=\"team_id\">\n <option value=\"auto\" >Automático → Equipa Norte</option>\n <option value=\"\" >Sem atribuição</option>\n \n <option value=\"1\" >Administrador de teste</option>\n \n <option value=\"2\" selected>Equipa Norte</option>\n \n <option value=\"3\" >Equipa Sul</option>\n \n <option value=\"4\" >Equipa partilhada</option>\n \n </select>\n <button class=\"secondary small\" type=\"submit\">Atribuir</button>\n </div>\n </form>\n \n </section>\n <p class=\"muted\">Atribuir aqui fecha qualquer pedido de troca em aberto sobre esse turno e avisa a nova equipa e a anterior.</p>\n <p class=\"muted\">«Automático» repõe a equipa que a escala dá a este dia — a ordem da rotação ou o padrão de noites — e volta a marcar o turno como automático.</p>\n</div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/assign?on=2030-01-05",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Registo de e-mails · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n \n \n <a href=\"/crr/admin/schedules\">Gerir escalas</a>\n <a href=\"/crr/admin/teams\">Equipas</a>\n <a href=\"/crr/admin/users\">Utilizadores</a>\n <a href=\"/crr/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n \n<div class=\"page-head\"><div><h1>Registo de e-mails</h1><p class=\"muted\">«ignorado» significa que a equipa não tem e-mail, que tem as notificações desligadas ou que o SMTP não está configurado.</p></div></div>\n<div class=\"table-wrap\"><table><thead><tr><th>Data e hora</th><th>Evento</th><th>Equipa</th><th>Destinatário</th><th>Estado</th><th>Assunto</th></tr></thead><tbody>\n<tr><td>07/10/2026 10:58</td><td>Troca revertida</td><td>Equipa Sul</td><td>b@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Troca de turno revertida</td></tr><tr><td>07/10/2026 10:58</td><td>Troca revertida</td><td>Equipa Norte</td><td>a@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Troca de turno revertida</td></tr><tr><td>07/10/2026 10:58</td><td>Troca aprovada</td><td>Equipa Sul</td><td>b@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Troca de turno aprovada</td></tr><tr><td>07/10/2026 10:58</td><td>Troca aprovada</td><td>Equipa Norte</td><td>a@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Troca de turno aprovada</td></tr><tr><td>07/10/2026 10:58</td><td>Troca aceite</td><td>Equipa Norte</td><td>a@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>A sua troca de turno foi aceite</td></tr><tr><td>07/10/2026 10:58</td><td>Troca aguarda aprovação</td><td>Administrador de teste</td><td>admin@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Uma troca de turno precisa de aprovação</td></tr><tr><td>07/10/2026 10:58</td><td>Pedido de troca</td><td>Equipa Sul</td><td>b@example.com</td><td><span class=\"status status-skipped\">ignorado</span></td><td>Troca de turnos: sáb 05 jan por sáb 12 jan – Rotação Phase 4</td></tr>\n</tbody></table></div>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/notifications",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/admin/schedules"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/month",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/admin/schedules/4?year=2030&month=1"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/schedules/4/apply-rotation",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/admin/schedules/4?year=2030&month=1"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/schedules/4/clear-from",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/admin/assign?on=2030-01-26"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/assign",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/admin/teams"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/teams",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/admin/teams"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/teams/5/update",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>Remover Equipa editada · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n \n \n <a href=\"/crr/admin/schedules\">Gerir escalas</a>\n <a href=\"/crr/admin/teams\">Equipas</a>\n <a href=\"/crr/admin/users\">Utilizadores</a>\n <a href=\"/crr/admin/notifications\">Registo de e-mails</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Administrador de teste</span>\n <span class=\"muted\">@phase4-admin</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n <div class=\"flash info\" role=\"status\">Equipa atualizada.</div>\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/crr/admin/teams\">Equipas</a></p>\n <h1>Remover equipa</h1>\n <p class=\"muted\">Isto é definitivo e não pode ser desfeito.</p>\n </div>\n</div>\n<section class=\"panel\">\n <h2>Equipa editada</h2>\n <p>Sem utilizador, apenas aparece na escala.</p>\n <p class=\"muted\">Só é possível remover quem ainda não tem turnos atribuídos. Primeiro transfira os turnos desta equipa para outras; assim o registo de quem trabalhou cada data mantém-se.</p>\n <p class=\"muted\">Ao remover, acontece o seguinte:</p>\n <ul class=\"link-list\">\n <li>0 posição(ões) saem das rotações.</li>\n \n \n <li>O histórico de e-mails é mantido, sem ligação a esta equipa.</li>\n </ul>\n <p class=\"muted\">Se só quiser que esta equipa deixe de receber turnos, desative-a em vez de a remover.</p>\n <div class=\"actions\">\n <form method=\"post\" action=\"/crr/admin/teams/5/delete\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"danger\" type=\"submit\">Remover definitivamente</button>\n </form>\n <a class=\"button secondary\" href=\"/crr/admin/teams\">Cancelar</a>\n </div>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/admin/teams/5/delete",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/admin/teams"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/teams/5/delete",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/admin/users"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/users",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/admin/users"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/users/4/update",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/admin/users"
|
||
},
|
||
"method": "POST",
|
||
"path": "/admin/users/4/delete",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/"
|
||
},
|
||
"method": "POST",
|
||
"path": "/account",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>A minha conta · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n <a href=\"/crr/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n <div class=\"flash error\" role=\"status\">A nova palavra-passe é igual à atual.</div>\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/crr/\">Rota</a></p>\n <h1>A minha conta</h1>\n <p class=\"muted\">Membro a · @member-a</p>\n \n <p class=\"muted\">Este utilizador entra em nome de 2 equipa(s): Equipa Norte, Equipa partilhada. Vê e controla os turnos de todas.</p>\n \n </div>\n</div>\n\n<section class=\"panel\">\n <h2>Alterar palavra-passe</h2>\n <form method=\"post\" action=\"/crr/account\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"password\" name=\"new_password\" minlength=\"4\" autocomplete=\"new-password\" required autofocus>\n <div><button class=\"primary\" type=\"submit\">Alterar palavra-passe</button></div>\n </form>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/account",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/"
|
||
},
|
||
"method": "POST",
|
||
"path": "/account",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "<!doctype html>\n<html lang=\"pt-PT\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta name=\"theme-color\" content=\"#ed1c24\">\n <title>A minha conta · Contínuos CRR</title>\n <link rel=\"icon\" type=\"image/png\" href=\"http://testserver/crr/brand/assets/favicon.png\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/tokens.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/base.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/brand/styles/components.css\">\n <link rel=\"stylesheet\" href=\"http://testserver/crr/static/app.css\">\n</head>\n<body>\n <header class=\"topbar\">\n <div class=\"container topbar-inner\">\n <a class=\"brand\" href=\"/crr/\" aria-label=\"Contínuos CRR — início\">\n <img class=\"brand-logo\" src=\"http://testserver/crr/brand/assets/logo.png\" alt=\"CRR\">\n <span class=\"brand-name\">Contínuos CRR</span>\n </a>\n <nav class=\"nav\" aria-label=\"Navegação principal\">\n \n <a href=\"/crr/swaps\">Trocas</a>\n <a href=\"/crr/export/schedule\">Exportar PDF</a>\n \n \n </nav>\n \n <div class=\"account\">\n <a class=\"account-link\" href=\"/crr/account\" title=\"A minha conta\">\n <span class=\"name-text\">Membro a</span>\n <span class=\"muted\">@member-a</span>\n </a>\n <form method=\"post\" action=\"/crr/logout\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <button class=\"link-button\" type=\"submit\">Sair</button>\n </form>\n </div>\n \n </div>\n </header>\n <main class=\"container\">\n \n <div class=\"flash info\" role=\"status\">Palavra-passe alterada.</div>\n \n \n<div class=\"page-head\">\n <div>\n <p class=\"eyebrow\"><a href=\"/crr/\">Rota</a></p>\n <h1>A minha conta</h1>\n <p class=\"muted\">Membro a · @member-a</p>\n \n <p class=\"muted\">Este utilizador entra em nome de 2 equipa(s): Equipa Norte, Equipa partilhada. Vê e controla os turnos de todas.</p>\n \n </div>\n</div>\n\n<section class=\"panel\">\n <h2>Alterar palavra-passe</h2>\n <form method=\"post\" action=\"/crr/account\" class=\"form-grid\">\n <input type=\"hidden\" name=\"csrf_token\" value=\"<csrf>\">\n <input type=\"password\" name=\"new_password\" minlength=\"4\" autocomplete=\"new-password\" required autofocus>\n <div><button class=\"primary\" type=\"submit\">Alterar palavra-passe</button></div>\n </form>\n</section>\n\n </main>\n</body>\n</html>",
|
||
"headers": {
|
||
"content-type": "text/html; charset=utf-8"
|
||
},
|
||
"method": "GET",
|
||
"path": "/account",
|
||
"status": 200
|
||
},
|
||
{
|
||
"body": "",
|
||
"headers": {
|
||
"location": "/crr/login"
|
||
},
|
||
"method": "POST",
|
||
"path": "/logout",
|
||
"status": 303
|
||
},
|
||
{
|
||
"body": "{\"detail\":\"É necessário iniciar sessão\"}",
|
||
"headers": {
|
||
"content-type": "application/json"
|
||
},
|
||
"method": "GET",
|
||
"path": "/account",
|
||
"status": 401
|
||
}
|
||
],
|
||
"rows": {
|
||
"alembic_version": [
|
||
{
|
||
"version_num": "0001_initial"
|
||
}
|
||
],
|
||
"assignments": [
|
||
{
|
||
"date": "2030-01-05",
|
||
"id": 1,
|
||
"note": "Revertida a troca #1 por Administrador de teste",
|
||
"schedule_id": 4,
|
||
"source": "manual",
|
||
"team_id": 2,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-12",
|
||
"id": 2,
|
||
"note": "Revertida a troca #1 por Administrador de teste",
|
||
"schedule_id": 4,
|
||
"source": "manual",
|
||
"team_id": 3,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-19",
|
||
"id": 3,
|
||
"note": null,
|
||
"schedule_id": 4,
|
||
"source": "swap",
|
||
"team_id": 2,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-26",
|
||
"id": 4,
|
||
"note": null,
|
||
"schedule_id": 4,
|
||
"source": "manual",
|
||
"team_id": 3,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-01",
|
||
"id": 5,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-02",
|
||
"id": 6,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-03",
|
||
"id": 7,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-04",
|
||
"id": 8,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-05",
|
||
"id": 9,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-06",
|
||
"id": 10,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-07",
|
||
"id": 11,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-08",
|
||
"id": 12,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-09",
|
||
"id": 13,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-10",
|
||
"id": 14,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-11",
|
||
"id": 15,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-12",
|
||
"id": 16,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-13",
|
||
"id": 17,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-14",
|
||
"id": 18,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-15",
|
||
"id": 19,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-16",
|
||
"id": 20,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-17",
|
||
"id": 21,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-18",
|
||
"id": 22,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-19",
|
||
"id": 23,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-20",
|
||
"id": 24,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-21",
|
||
"id": 25,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-22",
|
||
"id": 26,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-23",
|
||
"id": 27,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-24",
|
||
"id": 28,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-25",
|
||
"id": 29,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-26",
|
||
"id": 30,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-27",
|
||
"id": 31,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-28",
|
||
"id": 32,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-29",
|
||
"id": 33,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-30",
|
||
"id": 34,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-31",
|
||
"id": 35,
|
||
"note": null,
|
||
"schedule_id": 1,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-05",
|
||
"id": 36,
|
||
"note": null,
|
||
"schedule_id": 2,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-12",
|
||
"id": 37,
|
||
"note": null,
|
||
"schedule_id": 2,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-19",
|
||
"id": 38,
|
||
"note": null,
|
||
"schedule_id": 2,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-26",
|
||
"id": 39,
|
||
"note": null,
|
||
"schedule_id": 2,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-06",
|
||
"id": 40,
|
||
"note": null,
|
||
"schedule_id": 3,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-13",
|
||
"id": 41,
|
||
"note": null,
|
||
"schedule_id": 3,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-20",
|
||
"id": 42,
|
||
"note": null,
|
||
"schedule_id": 3,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
},
|
||
{
|
||
"date": "2030-01-27",
|
||
"id": 43,
|
||
"note": null,
|
||
"schedule_id": 3,
|
||
"source": "generated",
|
||
"team_id": null,
|
||
"updated_at": "<timestamp>"
|
||
}
|
||
],
|
||
"monthly_patterns": [],
|
||
"notification_logs": [
|
||
{
|
||
"body": "Equipa Norte propõe trocar o turno de Rotação Phase 4 de 5 de janeiro de 2030 pelo seu turno de 12 de janeiro de 2030.\n\nTeste de troca\n\nAbrir a escala: http://testserver/swaps",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_requested",
|
||
"id": 1,
|
||
"recipient": "b@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Troca de turnos: sáb 05 jan por sáb 12 jan – Rotação Phase 4",
|
||
"team_id": 3
|
||
},
|
||
{
|
||
"body": "Equipa Sul aceitou a troca de Rotação Phase 4 de 5 de janeiro de 2030 por 12 de janeiro de 2030, pedida por Equipa Norte. Aprove em http://testserver/swaps",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_needs_approval",
|
||
"id": 2,
|
||
"recipient": "admin@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Uma troca de turno precisa de aprovação",
|
||
"team_id": 1
|
||
},
|
||
{
|
||
"body": "Equipa Sul aceitou o seu pedido. Está agora à espera da aprovação da gestão.",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_accepted",
|
||
"id": 3,
|
||
"recipient": "a@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "A sua troca de turno foi aceite",
|
||
"team_id": 2
|
||
},
|
||
{
|
||
"body": "Administrador de teste aprovou a troca de Rotação Phase 4: Equipa Sul fica com o turno de 5 de janeiro de 2030 e Equipa Norte com o de 12 de janeiro de 2030.",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_approved",
|
||
"id": 4,
|
||
"recipient": "a@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Troca de turno aprovada",
|
||
"team_id": 2
|
||
},
|
||
{
|
||
"body": "Administrador de teste aprovou a troca de Rotação Phase 4: Equipa Sul fica com o turno de 5 de janeiro de 2030 e Equipa Norte com o de 12 de janeiro de 2030.",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_approved",
|
||
"id": 5,
|
||
"recipient": "b@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Troca de turno aprovada",
|
||
"team_id": 3
|
||
},
|
||
{
|
||
"body": "Administrador de teste reverteu a troca de Rotação Phase 4. Equipa Norte volta a 5 de janeiro de 2030 e Equipa Sul volta a 12 de janeiro de 2030.\n\nVer a escala: http://testserver/",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_reverted",
|
||
"id": 6,
|
||
"recipient": "a@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Troca de turno revertida",
|
||
"team_id": 2
|
||
},
|
||
{
|
||
"body": "Administrador de teste reverteu a troca de Rotação Phase 4. Equipa Norte volta a 5 de janeiro de 2030 e Equipa Sul volta a 12 de janeiro de 2030.\n\nVer a escala: http://testserver/",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "swap_reverted",
|
||
"id": 7,
|
||
"recipient": "b@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Troca de turno revertida",
|
||
"team_id": 3
|
||
},
|
||
{
|
||
"body": "Já não está atribuído ao turno de Rotação Phase 4 a 26 de janeiro de 2030.\n\nVer a escala: http://testserver/",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "assignment_changed",
|
||
"id": 8,
|
||
"recipient": "b@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Escala atualizada: sáb 26 jan – Rotação Phase 4",
|
||
"team_id": 3
|
||
},
|
||
{
|
||
"body": "Está atribuído ao turno de Rotação Phase 4 a 26 de janeiro de 2030.\n\nVer a escala: http://testserver/",
|
||
"channel": "email",
|
||
"created_at": "<timestamp>",
|
||
"error": "SMTP_HOST não está configurado",
|
||
"event_type": "assignment_updated",
|
||
"id": 9,
|
||
"recipient": "b@example.com",
|
||
"sent_at": null,
|
||
"status": "skipped",
|
||
"subject": "Escala atualizada: sáb 26 jan – Rotação Phase 4",
|
||
"team_id": 3
|
||
}
|
||
],
|
||
"rotation_members": [
|
||
{
|
||
"id": 1,
|
||
"position": 1,
|
||
"schedule_id": 4,
|
||
"team_id": 2
|
||
},
|
||
{
|
||
"id": 2,
|
||
"position": 2,
|
||
"schedule_id": 4,
|
||
"team_id": 3
|
||
}
|
||
],
|
||
"schedules": [
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"end_time": "00:00:00.000000",
|
||
"id": 1,
|
||
"is_active": 1,
|
||
"name": "Noite",
|
||
"requires_manager_approval": 1,
|
||
"rotation_anchor_date": null,
|
||
"schedule_type": "fixed",
|
||
"slug": "night",
|
||
"start_time": "20:30:00.000000",
|
||
"weekdays": "0,1,2,3,4,5,6"
|
||
},
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"end_time": "16:00:00.000000",
|
||
"id": 2,
|
||
"is_active": 1,
|
||
"name": "Almoço - Sábados",
|
||
"requires_manager_approval": 1,
|
||
"rotation_anchor_date": "2024-01-06",
|
||
"schedule_type": "rotation",
|
||
"slug": "saturday-lunch",
|
||
"start_time": "13:00:00.000000",
|
||
"weekdays": "5"
|
||
},
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"end_time": "16:00:00.000000",
|
||
"id": 3,
|
||
"is_active": 1,
|
||
"name": "Almoço - Domingo",
|
||
"requires_manager_approval": 1,
|
||
"rotation_anchor_date": "2024-01-07",
|
||
"schedule_type": "rotation",
|
||
"slug": "sunday-lunch",
|
||
"start_time": "13:00:00.000000",
|
||
"weekdays": "6"
|
||
},
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"end_time": "14:00:00.000000",
|
||
"id": 4,
|
||
"is_active": 1,
|
||
"name": "Rotação Phase 4",
|
||
"requires_manager_approval": 1,
|
||
"rotation_anchor_date": "2030-01-05",
|
||
"schedule_type": "rotation",
|
||
"slug": "phase4-rotation",
|
||
"start_time": "12:00:00.000000",
|
||
"weekdays": "5"
|
||
}
|
||
],
|
||
"swap_requests": [
|
||
{
|
||
"accepted_team_id": 3,
|
||
"assignment_id": 1,
|
||
"created_at": "<timestamp>",
|
||
"id": 1,
|
||
"message": "Teste de troca",
|
||
"requester_id": 2,
|
||
"status": "reverted",
|
||
"target_assignment_id": 2,
|
||
"target_team_id": 3,
|
||
"updated_at": "<timestamp>"
|
||
}
|
||
],
|
||
"teams": [
|
||
{
|
||
"calendar_token": null,
|
||
"created_at": "<timestamp>",
|
||
"email": "admin@example.com",
|
||
"id": 1,
|
||
"is_active": 1,
|
||
"name": "Administrador de teste",
|
||
"notify_email": 1,
|
||
"phone": null,
|
||
"user_id": 1
|
||
},
|
||
{
|
||
"calendar_token": "phase4-feed-a",
|
||
"created_at": "<timestamp>",
|
||
"email": "a@example.com",
|
||
"id": 2,
|
||
"is_active": 1,
|
||
"name": "Equipa Norte",
|
||
"notify_email": 1,
|
||
"phone": null,
|
||
"user_id": 2
|
||
},
|
||
{
|
||
"calendar_token": "phase4-feed-b",
|
||
"created_at": "<timestamp>",
|
||
"email": "b@example.com",
|
||
"id": 3,
|
||
"is_active": 1,
|
||
"name": "Equipa Sul",
|
||
"notify_email": 1,
|
||
"phone": null,
|
||
"user_id": 3
|
||
},
|
||
{
|
||
"calendar_token": "phase4-feed-c",
|
||
"created_at": "<timestamp>",
|
||
"email": "c@example.com",
|
||
"id": 4,
|
||
"is_active": 1,
|
||
"name": "Equipa partilhada",
|
||
"notify_email": 1,
|
||
"phone": null,
|
||
"user_id": 2
|
||
}
|
||
],
|
||
"users": [
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"id": 1,
|
||
"is_active": 1,
|
||
"is_admin": 1,
|
||
"name": "Administrador de teste",
|
||
"password_hash": "<password-hash>",
|
||
"username": "phase4-admin"
|
||
},
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"id": 2,
|
||
"is_active": 1,
|
||
"is_admin": 0,
|
||
"name": "Membro a",
|
||
"password_hash": "<password-hash>",
|
||
"username": "member-a"
|
||
},
|
||
{
|
||
"created_at": "<timestamp>",
|
||
"id": 3,
|
||
"is_active": 1,
|
||
"is_admin": 0,
|
||
"name": "Membro b",
|
||
"password_hash": "<password-hash>",
|
||
"username": "member-b"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
}
|
||
}
|