├── apps ├── api │ ├── public │ │ ├── cad │ │ │ └── .gitkeep │ │ ├── bleeter │ │ │ └── .gitkeep │ │ ├── citizens │ │ │ └── .gitkeep │ │ ├── pets │ │ │ └── .gitkeep │ │ ├── units │ │ │ └── .gitkeep │ │ ├── users │ │ │ └── .gitkeep │ │ └── values │ │ │ └── .gitkeep │ ├── prisma │ │ └── migrations │ │ │ ├── 20221003074925_ │ │ │ └── migration.sql │ │ │ ├── 20220507133405_ │ │ │ └── migration.sql │ │ │ ├── 20220601123540_ │ │ │ └── migration.sql │ │ │ ├── 20220603143408_ │ │ │ └── migration.sql │ │ │ ├── 20220414054943_ │ │ │ └── migration.sql │ │ │ ├── 20220618190815_ │ │ │ └── migration.sql │ │ │ ├── 20220803063610_ │ │ │ └── migration.sql │ │ │ ├── 20220219062422_ │ │ │ └── migration.sql │ │ │ ├── 20220506153633_ │ │ │ └── migration.sql │ │ │ ├── 20220603141537_ │ │ │ └── migration.sql │ │ │ ├── 20220803132819_ │ │ │ └── migration.sql │ │ │ ├── 20220107063437_ │ │ │ └── migration.sql │ │ │ ├── 20220306075835_ │ │ │ └── migration.sql │ │ │ ├── 20220314152458_ │ │ │ └── migration.sql │ │ │ ├── 20220317185041_ │ │ │ └── migration.sql │ │ │ ├── 20220404184430_ │ │ │ └── migration.sql │ │ │ ├── 20220521070942_ │ │ │ └── migration.sql │ │ │ ├── 20220614122254_ │ │ │ └── migration.sql │ │ │ ├── 20220723075107_ │ │ │ └── migration.sql │ │ │ ├── 20220807081952_ │ │ │ └── migration.sql │ │ │ ├── 20230622121133_pet_image │ │ │ └── migration.sql │ │ │ ├── 20230925141841_open_law_book │ │ │ └── migration.sql │ │ │ ├── 20211101070954_ │ │ │ └── migration.sql │ │ │ ├── 20211231083635_ │ │ │ └── migration.sql │ │ │ ├── 20220110151007_ │ │ │ └── migration.sql │ │ │ ├── 20220218141237_ │ │ │ └── migration.sql │ │ │ ├── 20230910082136_cad_time_zone │ │ │ └── migration.sql │ │ │ ├── 20220405055429_ │ │ │ └── migration.sql │ │ │ ├── 20220619123615_ │ │ │ └── migration.sql │ │ │ ├── 20220713073123_ │ │ │ └── migration.sql │ │ │ ├── 20220929141301_ │ │ │ └── migration.sql │ │ │ ├── 20230302182638_signal_100_citizen │ │ │ └── migration.sql │ │ │ ├── 20230413172828_hospital_services │ │ │ └── migration.sql │ │ │ ├── 20220121131926_ │ │ │ └── migration.sql │ │ │ ├── 20220220065106_ │ │ │ └── migration.sql │ │ │ ├── 20220413055643_ │ │ │ └── migration.sql │ │ │ ├── 20220913151619_ │ │ │ └── migration.sql │ │ │ ├── 20221017145249_violation_counts │ │ │ └── migration.sql │ │ │ ├── 20220508135605_ │ │ │ └── migration.sql │ │ │ ├── 20220616093906_ │ │ │ └── migration.sql │ │ │ ├── 20220911181259_ │ │ │ └── migration.sql │ │ │ ├── 20230118165112_unavailable_should_do_type │ │ │ └── migration.sql │ │ │ ├── 20230120152020_extra_fields_call911 │ │ │ └── migration.sql │ │ │ ├── 20230329163720_force_account_password │ │ │ └── migration.sql │ │ │ ├── 20230722075127_records_rich_text │ │ │ └── migration.sql │ │ │ ├── 20231006080836_other_license_category │ │ │ └── migration.sql │ │ │ ├── 20231007073203_citizen_record_payments │ │ │ └── migration.sql │ │ │ ├── 20231121171914_default_timezone │ │ │ └── migration.sql │ │ │ ├── 20211222082436_ │ │ │ └── migration.sql │ │ │ ├── 20220214151127_ │ │ │ └── migration.sql │ │ │ ├── 20220905180542_ │ │ │ └── migration.sql │ │ │ ├── 20221117152438_citizen_records_creation │ │ │ └── migration.sql │ │ │ ├── 20230209165735_multi_discord_guild_support │ │ │ └── migration.sql │ │ │ ├── 20230526154910_extra_fields_cad_feature │ │ │ └── migration.sql │ │ │ ├── 20230920140633_status_value_text_color │ │ │ └── migration.sql │ │ │ ├── 20220918075522_ │ │ │ └── migration.sql │ │ │ ├── 20221028163020_speech_voice_setting │ │ │ └── migration.sql │ │ │ ├── 20221214130600_cancel_expungement_requests │ │ │ └── migration.sql │ │ │ ├── 20230105184737_extra_fields_divisionvalue │ │ │ └── migration.sql │ │ │ ├── 20230607121959_community_service_option │ │ │ └── migration.sql │ │ │ ├── 20230616115711_required_citizen_image_optional │ │ │ └── migration.sql │ │ │ ├── 20230627062827_leo_editable_citizen_profile │ │ │ └── migration.sql │ │ │ ├── 20240106074701_warrant_case_number │ │ │ └── migration.sql │ │ │ ├── 20220220123028_ │ │ │ └── migration.sql │ │ │ ├── 20220227101539_radiochannelid_combined │ │ │ └── migration.sql │ │ │ ├── 20220301110033_call_via_dispatch │ │ │ └── migration.sql │ │ │ ├── 20220301182948_admin_citizen_deletion │ │ │ └── migration.sql │ │ │ ├── 20220419144817_ │ │ │ └── migration.sql │ │ │ ├── 20220420114151_ │ │ │ └── migration.sql │ │ │ ├── 20220420121656_ │ │ │ └── migration.sql │ │ │ ├── 20221025142353_call_911_signal_100 │ │ │ └── migration.sql │ │ │ ├── 20221214124333_extra_fields_departmentvalue │ │ │ └── migration.sql │ │ │ ├── 20221231124604_case_number_template │ │ │ └── migration.sql │ │ │ ├── 20230118141608_nullable_executor_audit_log │ │ │ └── migration.sql │ │ │ ├── 20230127171334_optional_vehicle_owner │ │ │ └── migration.sql │ │ │ ├── 20230716142458_medical_record_desc_data │ │ │ └── migration.sql │ │ │ ├── 20230324174132_default_permissions │ │ │ └── migration.sql │ │ │ ├── 20230615113018_custom_template_department_value │ │ │ └── migration.sql │ │ │ ├── 20230907142434_impounded_vehicle_description │ │ │ └── migration.sql │ │ │ ├── 20231202095501_description_emergency_value │ │ │ └── migration.sql │ │ │ ├── 20220305071718_ │ │ │ └── migration.sql │ │ │ ├── 20230414111403_user_developer_mode │ │ │ └── migration.sql │ │ │ ├── 20230517120247_medical_records_citizen_manageable │ │ │ └── migration.sql │ │ │ ├── 20231110181854_extra_fields_emergency_vehicle │ │ │ └── migration.sql │ │ │ ├── migration_lock.toml │ │ │ ├── 20211103063708_ │ │ │ └── migration.sql │ │ │ ├── 20221011142755_is_primary_penalcode_1139 │ │ │ └── migration.sql │ │ │ ├── 20230117055125_last_sync_inactivity │ │ │ └── migration.sql │ │ │ ├── 20230705071011_multiple_departments_units_per_user │ │ │ └── migration.sql │ │ │ ├── 20220222175328_ │ │ │ └── migration.sql │ │ │ ├── 20220419145730_ │ │ │ └── migration.sql │ │ │ ├── 20230110152253_last_seen_on_user │ │ │ └── migration.sql │ │ │ ├── 20211214110448_ │ │ │ └── migration.sql │ │ │ ├── 20220108103934_ │ │ │ └── migration.sql │ │ │ ├── 20221122155409_manage_awards_qualifications_permissions │ │ │ └── migration.sql │ │ │ ├── 20220310152403_ │ │ │ └── migration.sql │ │ │ ├── 20221207131235_active_dispatchers_activity_timeout │ │ │ └── migration.sql │ │ │ ├── 20220403064022_ │ │ │ └── migration.sql │ │ │ ├── 20221222094403_missing_citizens │ │ │ └── migration.sql │ │ │ ├── 20220428161153_ │ │ │ └── migration.sql │ │ │ ├── 20220903095803_ │ │ │ └── migration.sql │ │ │ ├── 20220407074655_ │ │ │ └── migration.sql │ │ │ ├── 20211230101100_ │ │ │ └── migration.sql │ │ │ ├── 20220110184116_ │ │ │ └── migration.sql │ │ │ ├── 20220220115325_ │ │ │ └── migration.sql │ │ │ ├── 20220715065900_ │ │ │ └── migration.sql │ │ │ ├── 20220123090306_ │ │ │ └── migration.sql │ │ │ ├── 20220503141835_ │ │ │ └── migration.sql │ │ │ ├── 20220829154218_ │ │ │ └── migration.sql │ │ │ ├── 20220409062923_ │ │ │ └── migration.sql │ │ │ ├── 20220427114851_ │ │ │ └── migration.sql │ │ │ ├── 20230123172736_call_911_approval │ │ │ └── migration.sql │ │ │ ├── 20221208105820_bureau_of_firearms │ │ │ └── migration.sql │ │ │ ├── 20220502143247_ │ │ │ └── migration.sql │ │ │ ├── 20220504153820_ │ │ │ └── migration.sql │ │ │ ├── 20221005123120_ │ │ │ └── migration.sql │ │ │ ├── 20230806071701_vehicle_model_images │ │ │ └── migration.sql │ │ │ ├── 20220125150836_ │ │ │ └── migration.sql │ │ │ ├── 20220514053844_ │ │ │ └── migration.sql │ │ │ ├── 20230330144749_change_business_owner │ │ │ └── migration.sql │ │ │ ├── 20220107123346_ │ │ │ └── migration.sql │ │ │ ├── 20220916160317_ │ │ │ └── migration.sql │ │ │ ├── 20230330175639_employee_permissions │ │ │ └── migration.sql │ │ │ ├── 20230706120138_signal_100_repeat_settings │ │ │ └── migration.sql │ │ │ ├── 20220817064415_ │ │ │ └── migration.sql │ │ │ ├── 20230615115745_user_defined_callsign_units │ │ │ └── migration.sql │ │ │ ├── 20230925150249_extended_ems_fd_incidents │ │ │ └── migration.sql │ │ │ ├── 20230821091842_comments_on_courthouse_requests │ │ │ └── migration.sql │ │ │ ├── 20231223155548_draft_records │ │ │ └── migration.sql │ │ │ ├── 20211102101051_ │ │ │ └── migration.sql │ │ │ ├── 20211105062536_ │ │ │ └── migration.sql │ │ │ ├── 20220205123934_ │ │ │ └── migration.sql │ │ │ ├── 20230415083206_remove_old_permissions_system │ │ │ └── migration.sql │ │ │ ├── 20211219152524_ │ │ │ └── migration.sql │ │ │ ├── 20211223170541_ │ │ │ └── migration.sql │ │ │ ├── 20230531172206_remove_deprecated_features_2 │ │ │ └── migration.sql │ │ │ ├── 20220427153022_ │ │ │ └── migration.sql │ │ │ ├── 20220222181405_ │ │ │ └── migration.sql │ │ │ ├── 20230129085118_license_number_template │ │ │ └── migration.sql │ │ │ ├── 20211109183706_ │ │ │ └── migration.sql │ │ │ ├── 20220811154606_ │ │ │ └── migration.sql │ │ │ ├── 20220307173213_ │ │ │ └── migration.sql │ │ │ ├── 20231017072316_pet_cascade_citizen │ │ │ └── migration.sql │ │ │ ├── 20220212080210_ │ │ │ └── migration.sql │ │ │ ├── 20220217150844_ │ │ │ └── migration.sql │ │ │ ├── 20220211155937_ │ │ │ └── migration.sql │ │ │ ├── 20220706072729_ │ │ │ └── migration.sql │ │ │ ├── 20220819063425_ │ │ │ └── migration.sql │ │ │ ├── 20211102163503_ │ │ │ └── migration.sql │ │ │ ├── 20220622063211_ │ │ │ └── migration.sql │ │ │ ├── 20221217072904_records_court_entry │ │ │ └── migration.sql │ │ │ ├── 20230927070429_vehicle_speed_record │ │ │ └── migration.sql │ │ │ ├── 20220224171140_ │ │ │ └── migration.sql │ │ │ ├── 20220430124313_ │ │ │ └── migration.sql │ │ │ ├── 20220801060848_ │ │ │ └── migration.sql │ │ │ ├── 20220219064214_ │ │ │ └── migration.sql │ │ │ ├── 20221019113803_auto_revoke_suspended_licenses │ │ │ └── migration.sql │ │ │ ├── 20220228062318_situation_code_incident │ │ │ └── migration.sql │ │ │ ├── 20220413130107_ │ │ │ └── migration.sql │ │ │ ├── 20221028153837_impound_lot_officer_impounded_by │ │ │ └── migration.sql │ │ │ ├── 20230221062301_business_records │ │ │ └── migration.sql │ │ │ ├── 20230412175911_custom_combined_unit_callsign │ │ │ └── migration.sql │ │ │ ├── 20220505141915_ │ │ │ └── migration.sql │ │ │ ├── 20211101093935_ │ │ │ └── migration.sql │ │ │ ├── 20220424090603_ │ │ │ └── migration.sql │ │ │ ├── 20220521060146_ │ │ │ └── migration.sql │ │ │ ├── 20230225102544_business_records_logs │ │ │ └── migration.sql │ │ │ ├── 20230306153731_dispatch_department_filter │ │ │ └── migration.sql │ │ │ ├── 20220306164233_ │ │ │ └── migration.sql │ │ │ ├── 20221108165656_cascade_impounded_vehicles │ │ │ └── migration.sql │ │ │ ├── 20220128083746_ │ │ │ └── migration.sql │ │ │ ├── 20230119152628_nullable_executor_audit_log │ │ │ └── migration.sql │ │ │ ├── 20230614145057_string_badge_number │ │ │ └── migration.sql │ │ │ ├── 20220408062540_ │ │ │ └── migration.sql │ │ │ ├── 20230531171901_remove_deprecated_features │ │ │ └── migration.sql │ │ │ ├── 20231021140807_deletable_penal_codes │ │ │ └── migration.sql │ │ │ ├── 20211229183021_ │ │ │ └── migration.sql │ │ │ ├── 20231023170026_disposition_code_call │ │ │ └── migration.sql │ │ │ ├── 20221224074854_vehicle_records │ │ │ └── migration.sql │ │ │ ├── 20230223061954_editable_ssn_vin_toggle │ │ │ └── migration.sql │ │ │ ├── 20220509170215_ │ │ │ └── migration.sql │ │ │ ├── 20230917161539_dashboard_layout_order │ │ │ └── migration.sql │ │ │ ├── 20230203153054_forced_oauth_connections │ │ │ └── migration.sql │ │ │ ├── 20230907153100_blacklisted_words │ │ │ └── migration.sql │ │ │ ├── 20230624061332_more_webhook_types │ │ │ └── migration.sql │ │ │ ├── 20230609080207_multi_live_map_urls │ │ │ └── migration.sql │ │ │ ├── 20231023170956_leo_ems_fd_incident_webhooks │ │ │ └── migration.sql │ │ │ ├── 20220219080856_ │ │ │ └── migration.sql │ │ │ ├── 20220210162134_ │ │ │ └── migration.sql │ │ │ ├── 20230215183336_records_connections │ │ │ └── migration.sql │ │ │ ├── 20211101110733_ │ │ │ └── migration.sql │ │ │ ├── 20211108153517_ │ │ │ └── migration.sql │ │ │ ├── 20221101091651_address_values │ │ │ └── migration.sql │ │ │ ├── 20230908142856_department_links │ │ │ └── migration.sql │ │ │ ├── 20230114081441_audit_logs │ │ │ └── migration.sql │ │ │ ├── 20230829143916_more_webhook_types │ │ │ └── migration.sql │ │ │ ├── 20221015061713_auto_911_call_events │ │ │ └── migration.sql │ │ │ ├── 20220311162452_ │ │ │ └── migration.sql │ │ │ ├── 20220529090004_ │ │ │ └── migration.sql │ │ │ ├── 20220828120916_ │ │ │ └── migration.sql │ │ │ ├── 20220223131255_ │ │ │ └── migration.sql │ │ │ ├── 20221015075722_citizen_license_numbers │ │ │ └── migration.sql │ │ │ ├── 20211222065948_ │ │ │ └── migration.sql │ │ │ ├── 20230227154019_raw_webhooks │ │ │ └── migration.sql │ │ │ ├── 20220122070726_ │ │ │ └── migration.sql │ │ │ ├── 20220228155753_leo_roles_discord │ │ │ └── migration.sql │ │ │ ├── 20220317151747_ │ │ │ └── migration.sql │ │ │ ├── 20220115075142_ │ │ │ └── migration.sql │ │ │ ├── 20220904060949_ │ │ │ └── migration.sql │ │ │ ├── 20220601115342_ │ │ │ └── migration.sql │ │ │ ├── 20220321151728_ │ │ │ └── migration.sql │ │ │ ├── 20211228091734_ │ │ │ └── migration.sql │ │ │ ├── 20221216054554_image_blur_data │ │ │ └── migration.sql │ │ │ ├── 20220505140647_ │ │ │ └── migration.sql │ │ │ ├── 20220602141240_ │ │ │ └── migration.sql │ │ │ ├── 20220310160602_ │ │ │ └── migration.sql │ │ │ ├── 20230222185840_admin_roles_arr │ │ │ └── migration.sql │ │ │ ├── 20220118171251_ │ │ │ └── migration.sql │ │ │ ├── 20221004064740_ │ │ │ └── migration.sql │ │ │ ├── 20221224115442_persistent_tones │ │ │ └── migration.sql │ │ │ ├── 20220526080330_ │ │ │ └── migration.sql │ │ │ ├── 20230718154053_weapon_flags │ │ │ └── migration.sql │ │ │ ├── 20220102070719_ │ │ │ └── migration.sql │ │ │ ├── 20220519162327_ │ │ │ └── migration.sql │ │ │ ├── 20220430073214_ │ │ │ └── migration.sql │ │ │ ├── 20211231080948_ │ │ │ └── migration.sql │ │ │ ├── 20220516165653_ │ │ │ └── migration.sql │ │ │ ├── 20220903062552_ │ │ │ └── migration.sql │ │ │ ├── 20231122144146_use_updated_at_units │ │ │ └── migration.sql │ │ │ ├── 20220221182451_ │ │ │ └── migration.sql │ │ │ ├── 20230128075737_custom_business_roles │ │ │ └── migration.sql │ │ │ └── 20230120161140_address_flag_values │ │ │ └── migration.sql │ ├── .npm-upgrade.json │ ├── src │ │ ├── lib │ │ │ ├── auth │ │ │ │ ├── post-auth.ts │ │ │ │ └── setUserPreferencesCookies.ts │ │ │ ├── discord │ │ │ │ ├── performDiscordRequest.ts │ │ │ │ ├── utils.ts │ │ │ │ └── config.ts │ │ │ ├── data │ │ │ │ └── prisma.ts │ │ │ ├── images │ │ │ │ ├── validate-image-url.ts │ │ │ │ └── get-image-webp-path.ts │ │ │ ├── citizen │ │ │ │ └── validate-ssn.ts │ │ │ └── leo │ │ │ │ └── records │ │ │ │ └── create-citizen-violations.ts │ │ ├── utils │ │ │ ├── findFirstSmallestInArray.ts │ │ │ ├── order-by.ts │ │ │ ├── businesses.ts │ │ │ ├── jwt.ts │ │ │ ├── file.ts │ │ │ └── generate-string.ts │ │ ├── exceptions │ │ │ ├── extended-not-found.ts │ │ │ ├── feature-not-enabled.ts │ │ │ └── extended-bad-request.ts │ │ ├── middlewares │ │ │ ├── active-deputy.ts │ │ │ └── active-officer.ts │ │ └── instrument.ts │ ├── scripts │ │ └── postbuild.mjs │ └── tests │ │ └── smallest-int-in-array.test.ts ├── client │ ├── public │ │ ├── map │ │ │ ├── ped.png │ │ │ ├── call.png │ │ │ ├── panic.gif │ │ │ ├── siren.gif │ │ │ ├── unit_ped.png │ │ │ ├── blips_texturesheet.png │ │ │ └── smart-motorways │ │ │ │ ├── 1.png │ │ │ │ ├── 2.png │ │ │ │ ├── 20.png │ │ │ │ ├── 3.png │ │ │ │ ├── 30.png │ │ │ │ ├── 40.png │ │ │ │ ├── 50.png │ │ │ │ ├── 60.png │ │ │ │ ├── 70.png │ │ │ │ ├── 80.png │ │ │ │ ├── 90.png │ │ │ │ ├── 100.png │ │ │ │ ├── 110.png │ │ │ │ ├── 120.png │ │ │ │ ├── 130.png │ │ │ │ ├── 140.png │ │ │ │ ├── 150.png │ │ │ │ ├── national_speed.png │ │ │ │ └── CREDITS.txt │ │ ├── sounds │ │ │ ├── signal100.mp3 │ │ │ ├── panic-button.mp3 │ │ │ └── added-to-call.mp3 │ │ ├── tiles │ │ │ ├── minimap_sea_0_0.webp │ │ │ ├── minimap_sea_0_1.webp │ │ │ ├── minimap_sea_1_0.webp │ │ │ ├── minimap_sea_1_1.webp │ │ │ ├── minimap_sea_2_0.webp │ │ │ └── minimap_sea_2_1.webp │ │ └── fonts │ │ │ └── Assistant-VariableFont_wght.ttf │ ├── postcss.config.js │ ├── types.d.ts │ ├── src │ │ ├── components │ │ │ ├── account │ │ │ │ └── images │ │ │ │ │ ├── full-row-color.png │ │ │ │ │ └── row-dot-color.png │ │ │ ├── shared │ │ │ │ ├── Table.tsx │ │ │ │ └── image-wrapper.tsx │ │ │ ├── dispatch │ │ │ │ └── map │ │ │ │ │ ├── smart-signs │ │ │ │ │ └── render-map-smart-signs.tsx │ │ │ │ │ └── smart-motorway-signs │ │ │ │ │ └── render-map-smart-motorway-signs.tsx │ │ │ ├── leo │ │ │ │ └── records-case-number-column.tsx │ │ │ ├── citizen │ │ │ │ └── manage-citizen-form │ │ │ │ │ └── create-officer-step.tsx │ │ │ └── editor │ │ │ │ └── elements │ │ │ │ └── leaf.tsx │ │ ├── lib │ │ │ ├── classNames.ts │ │ │ ├── i18n │ │ │ │ └── getNextI18nConfig.ts │ │ │ ├── table │ │ │ │ └── get-contrasting-text-color.ts │ │ │ └── values │ │ │ │ └── normalize-value.ts │ │ ├── types │ │ │ └── dnd-actions.ts │ │ ├── styles │ │ │ ├── nprogress.css │ │ │ └── fonts.css │ │ ├── hooks │ │ │ ├── global │ │ │ │ ├── components │ │ │ │ │ └── socket-error-component.tsx │ │ │ │ └── useAreaOfPlay.ts │ │ │ ├── shared │ │ │ │ ├── use-debounced-value.ts │ │ │ │ └── useTemporaryItem.ts │ │ │ ├── use-format-case-number.ts │ │ │ ├── useImageUrl.ts │ │ │ ├── leo │ │ │ │ └── use-get-user-officers.ts │ │ │ ├── ems-fd │ │ │ │ └── use-get-user-deputies.ts │ │ │ ├── use-invalidate-query.ts │ │ │ └── usePermission.ts │ │ ├── pages │ │ │ ├── officer │ │ │ │ └── supervisor │ │ │ │ │ └── dl-exams.tsx │ │ │ ├── index.tsx │ │ │ └── api-docs.tsx │ │ └── state │ │ │ ├── citizen │ │ │ └── pets-state.ts │ │ │ ├── search │ │ │ ├── weapon-search-state.ts │ │ │ ├── vehicle-search-state.ts │ │ │ └── name-search-state.ts │ │ │ ├── ems-fd-state.ts │ │ │ └── leo-state.ts │ ├── next-env.d.ts │ ├── i18n.config.mjs │ ├── locales │ │ ├── cn │ │ │ ├── truck-logs.json │ │ │ ├── bleeter.json │ │ │ ├── courthouse.json │ │ │ ├── ems-fd.json │ │ │ └── auth.json │ │ ├── tc │ │ │ ├── truck-logs.json │ │ │ ├── bleeter.json │ │ │ ├── courthouse.json │ │ │ ├── ems-fd.json │ │ │ └── auth.json │ │ ├── zh-CN │ │ │ ├── truck-logs.json │ │ │ └── bleeter.json │ │ ├── cs-CZ │ │ │ ├── bleeter.json │ │ │ └── truck-logs.json │ │ ├── nl-BE │ │ │ ├── bleeter.json │ │ │ └── truck-logs.json │ │ ├── en │ │ │ └── truck-logs.json │ │ ├── en-gb │ │ │ └── truck-logs.json │ │ ├── pt-BR │ │ │ ├── bleeter.json │ │ │ └── truck-logs.json │ │ ├── sv │ │ │ └── truck-logs.json │ │ ├── fr-FR │ │ │ └── truck-logs.json │ │ ├── de-DE │ │ │ └── truck-logs.json │ │ └── ru │ │ │ └── truck-logs.json │ └── tests │ │ ├── getTranslations.test.ts │ │ └── classNames.test.ts └── README.md ├── .github ├── FUNDING.yml ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ └── 2.feature_request.yml ├── pull_request_template.md ├── SECURITY.md └── workflows │ └── cancel.yml ├── packages ├── ui │ ├── src │ │ ├── index.tsx │ │ ├── components │ │ │ ├── dnd │ │ │ │ ├── index.tsx │ │ │ │ └── dnd-provider.tsx │ │ │ ├── stories │ │ │ │ ├── helpers │ │ │ │ │ ├── loader.stories.tsx │ │ │ │ │ ├── error-message.stories.tsx │ │ │ │ │ └── label.stories.tsx │ │ │ │ ├── status │ │ │ │ │ ├── infofield.stories.tsx │ │ │ │ │ └── full-date.stories.tsx │ │ │ │ ├── fields │ │ │ │ │ ├── date-picker-field.stories.tsx │ │ │ │ │ ├── checkbox.stories.tsx │ │ │ │ │ └── switch-field.stories.tsx │ │ │ │ └── editors │ │ │ │ │ └── json-editor.stories.tsx │ │ │ ├── multi-form │ │ │ │ └── multi-form-step.tsx │ │ │ ├── inputs │ │ │ │ └── checkbox.tsx │ │ │ ├── loader.tsx │ │ │ ├── error-message.tsx │ │ │ ├── form-row.tsx │ │ │ ├── breadcrumbs │ │ │ │ └── breadcrumbs.tsx │ │ │ └── infofield.tsx │ │ ├── hooks │ │ │ └── select │ │ │ │ └── README.md │ │ ├── tailwind.css │ │ └── context │ │ │ └── radio-field-context.tsx │ ├── postcss.config.cjs │ ├── tsconfig.json │ └── .storybook │ │ └── main.ts ├── utils │ ├── src │ │ ├── index.ts │ │ ├── api-url.ts │ │ ├── utils │ │ │ ├── prefix-number.ts │ │ │ └── replace-template-variables.ts │ │ └── editor │ │ │ ├── index.ts │ │ │ └── slate-data-to-string.ts │ ├── README.md │ ├── vitest.config.ts │ ├── tsconfig.json │ └── tests │ │ └── version.test.ts ├── schemas │ ├── README.md │ ├── tsconfig.json │ └── src │ │ ├── admin │ │ ├── values.ts │ │ └── import │ │ │ ├── weapons.ts │ │ │ └── vehicles.ts │ │ ├── truck-log.ts │ │ ├── bleeter.ts │ │ ├── dispatch │ │ ├── bolos.ts │ │ └── index.ts │ │ ├── index.ts │ │ └── tow.ts ├── types │ ├── README.md │ ├── tsconfig.json │ └── src │ │ └── lib │ │ └── cad-feature.ts ├── permissions │ ├── src │ │ └── defaults │ │ │ ├── index.ts │ │ │ └── citizen.ts │ ├── README.md │ └── tsconfig.json ├── audit-logger │ ├── README.md │ ├── src │ │ └── index.ts │ └── tsconfig.json ├── config │ ├── README.md │ ├── tsconfig.json │ └── src │ │ ├── routes.ts │ │ └── index.ts └── README.md ├── pnpm-workspace.yaml ├── .npmrc ├── .husky └── pre-commit ├── commitlint.config.js ├── .dockerignore ├── .prettierrc ├── .vscode ├── settings.json └── extensions.json ├── .eslintignore ├── lerna.json ├── .deepsource.toml ├── .editorconfig ├── docker-compose.yml ├── scripts └── validate.mjs ├── .gitignore └── Dockerfile /apps/api/public/cad/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/api/public/bleeter/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/api/public/citizens/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/api/public/pets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/api/public/units/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/api/public/users/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/api/public/values/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: casperiv0 2 | -------------------------------------------------------------------------------- /packages/ui/src/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./components/index"; 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | - "packages/*" 4 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | legacy-peer-deps=true 2 | auto-install-peers=true 3 | package-manager-strict=false -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ["@commitlint/config-conventional"] }; 2 | -------------------------------------------------------------------------------- /packages/utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./callsign"; 2 | export * from "./typeguards"; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221003074925_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'TONES'; 3 | -------------------------------------------------------------------------------- /apps/client/public/map/ped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/ped.png -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220507133405_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'STEAM_OAUTH'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220601123540_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'LEO_TICKETS'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220603143408_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'LEO_BAIL'; 3 | -------------------------------------------------------------------------------- /apps/client/public/map/call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/call.png -------------------------------------------------------------------------------- /apps/client/public/map/panic.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/panic.gif -------------------------------------------------------------------------------- /apps/client/public/map/siren.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/siren.gif -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | *.log 4 | *.env 5 | Dockerfile 6 | .dockerignore 7 | __tests__/ 8 | coverage/ 9 | dist/ -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220414054943_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'BADGE_NUMBERS'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220618190815_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" ADD COLUMN "locale" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220803063610_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'PANIC_BUTTON'; 3 | -------------------------------------------------------------------------------- /apps/client/public/map/unit_ped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/unit_ped.png -------------------------------------------------------------------------------- /packages/schemas/README.md: -------------------------------------------------------------------------------- 1 | # @snailycad/schemas 2 | 3 | This is a module that is used to validate forms and API requests. 4 | -------------------------------------------------------------------------------- /packages/types/README.md: -------------------------------------------------------------------------------- 1 | # @snailycad/types 2 | 3 | This is a module that is used to share database types accross the CAD. 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220219062422_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'ALLOW_REGULAR_LOGIN'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220506153633_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'COMMON_CITIZEN_CARDS'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220603141537_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "TruckLog" ADD COLUMN "notes" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220803132819_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "DiscordWebhookType" ADD VALUE 'WARRANTS'; 3 | -------------------------------------------------------------------------------- /packages/permissions/src/defaults/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./admin"; 2 | export * from "./leo"; 3 | export * from "./citizen"; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220107063437_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Citizen" ADD COLUMN "occupation" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220306075835_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "ValueLicenseType" ADD VALUE 'INSURANCE_STATUS'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220314152458_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "DriversLicenseCategoryType" ADD VALUE 'FIREARM'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220317185041_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" ADD COLUMN "permissions" TEXT[]; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220404184430_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "LeoIncident" ADD COLUMN "postal" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220521070942_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'CREATE_USER_CITIZEN_LEO'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220614122254_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Citizen" ADD COLUMN "appearance" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220723075107_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'CITIZEN_DELETE_ON_DEAD'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220807081952_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Citizen" ADD COLUMN "additionalInfo" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230622121133_pet_image/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Pet" ADD COLUMN "imageId" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230925141841_open_law_book/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'OPEN_LAW_BOOK'; 3 | -------------------------------------------------------------------------------- /apps/client/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/client/public/sounds/signal100.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/sounds/signal100.mp3 -------------------------------------------------------------------------------- /packages/permissions/README.md: -------------------------------------------------------------------------------- 1 | # @snailycad/permissions 2 | 3 | This is a module to manage user permissions accross the codebase. 4 | -------------------------------------------------------------------------------- /packages/ui/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211101070954_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'ALLOW_DUPLICATE_CITIZEN_NAMES'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211231083635_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'DISALLOW_TEXTFIELD_SELECTION'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220110151007_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "PenalCodeGroup" ADD COLUMN "position" INTEGER; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220218141237_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'ALLOW_CITIZEN_UPDATE_LICENSE'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230910082136_cad_time_zone/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "cad" ADD COLUMN "timeZone" TEXT; 3 | -------------------------------------------------------------------------------- /apps/client/public/sounds/panic-button.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/sounds/panic-button.mp3 -------------------------------------------------------------------------------- /packages/ui/src/components/dnd/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./dnd-provider"; 2 | export * from "./draggable"; 3 | export * from "./droppable"; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220405055429_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Call911" ADD COLUMN "caseNumber" SERIAL NOT NULL; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220619123615_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "cadOGDescription" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220713073123_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "RegisteredVehicle" ADD COLUMN "appearance" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220929141301_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Record" ADD COLUMN "caseNumber" SERIAL NOT NULL; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230302182638_signal_100_citizen/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'SIGNAL_100_CITIZEN'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230413172828_hospital_services/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'HOSPITAL_SERVICES'; 3 | -------------------------------------------------------------------------------- /apps/client/public/map/blips_texturesheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/blips_texturesheet.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/1.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/2.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/20.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/3.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/30.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/40.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/50.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/60.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/70.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/80.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/90.png -------------------------------------------------------------------------------- /apps/client/public/sounds/added-to-call.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/sounds/added-to-call.mp3 -------------------------------------------------------------------------------- /apps/client/public/tiles/minimap_sea_0_0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/tiles/minimap_sea_0_0.webp -------------------------------------------------------------------------------- /apps/client/public/tiles/minimap_sea_0_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/tiles/minimap_sea_0_1.webp -------------------------------------------------------------------------------- /apps/client/public/tiles/minimap_sea_1_0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/tiles/minimap_sea_1_0.webp -------------------------------------------------------------------------------- /apps/client/public/tiles/minimap_sea_1_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/tiles/minimap_sea_1_1.webp -------------------------------------------------------------------------------- /apps/client/public/tiles/minimap_sea_2_0.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/tiles/minimap_sea_2_0.webp -------------------------------------------------------------------------------- /apps/client/public/tiles/minimap_sea_2_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/tiles/minimap_sea_2_1.webp -------------------------------------------------------------------------------- /apps/client/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module "hex-color-regex" { 2 | export default function hexColorRegex(options?: { struct: true }): RegExp; 3 | } 4 | -------------------------------------------------------------------------------- /packages/utils/README.md: -------------------------------------------------------------------------------- 1 | # @snailycad/utils 2 | 3 | This is a module that is used to house utility functions used both in the client and API. 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220121131926_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "maxOfficersPerUser" INTEGER; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220220065106_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" ADD COLUMN "lastDiscordSyncTimestamp" TIMESTAMP(3); 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220413055643_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "inactivityTimeout" INTEGER; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220913151619_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "CallTypeValue" ALTER COLUMN "priority" SET DATA TYPE TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221017145249_violation_counts/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Violation" ADD COLUMN "counts" INTEGER; 3 | -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/100.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/110.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/110.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/120.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/130.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/130.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/140.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/140.png -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/150.png -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220508135605_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DriversLicenseCategoryValue" ADD COLUMN "description" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220616093906_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Value" ADD COLUMN "isDisabled" BOOLEAN NOT NULL DEFAULT false; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220911181259_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "AssignedUnit" ADD COLUMN "isPrimary" BOOLEAN DEFAULT false; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230118165112_unavailable_should_do_type/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "ShouldDoType" ADD VALUE 'UNAVAILABLE'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230120152020_extra_fields_call911/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Call911" ADD COLUMN "extraFields" JSONB; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230329163720_force_account_password/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'FORCE_ACCOUNT_PASSWORD'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230722075127_records_rich_text/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Record" ADD COLUMN "descriptionData" JSONB; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231006080836_other_license_category/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "DriversLicenseCategoryType" ADD VALUE 'OTHER'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231007073203_citizen_record_payments/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'CITIZEN_RECORD_PAYMENTS'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231121171914_default_timezone/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "cad" ALTER COLUMN "timeZone" SET DEFAULT 'UTC'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211222082436_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "roleplayEnabled" BOOLEAN DEFAULT true; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220214151127_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "maxDepartmentsEachPerUser" INTEGER; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220905180542_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "GTAMapPosition" ALTER COLUMN "heading" SET DATA TYPE DOUBLE PRECISION; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221117152438_citizen_records_creation/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'CITIZEN_CREATION_RECORDS'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230209165735_multi_discord_guild_support/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DiscordRole" ADD COLUMN "guildId" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230526154910_extra_fields_cad_feature/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "CadFeature" ADD COLUMN "extraFields" JSONB; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230920140633_status_value_text_color/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "StatusValue" ADD COLUMN "textColor" TEXT; 3 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "endOfLine": "auto", 3 | "semi": true, 4 | "trailingComma": "all", 5 | "singleQuote": false, 6 | "printWidth": 100, 7 | "tabWidth": 2 8 | } -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220918075522_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "UserSoundSettings" ADD COLUMN "speech" BOOLEAN NOT NULL DEFAULT true; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221028163020_speech_voice_setting/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "UserSoundSettings" ADD COLUMN "speechVoice" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221214130600_cancel_expungement_requests/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "ExpungementRequestStatus" ADD VALUE 'CANCELED'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230105184737_extra_fields_divisionvalue/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DivisionValue" ADD COLUMN "extraFields" JSONB; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230607121959_community_service_option/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Violation" ADD COLUMN "communityService" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230616115711_required_citizen_image_optional/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'REQUIRED_CITIZEN_IMAGE'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230627062827_leo_editable_citizen_profile/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'LEO_EDITABLE_CITIZEN_PROFILE'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20240106074701_warrant_case_number/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Warrant" ADD COLUMN "caseNumber" SERIAL NOT NULL; 3 | -------------------------------------------------------------------------------- /packages/audit-logger/README.md: -------------------------------------------------------------------------------- 1 | # @snailycad/audit-logger 2 | 3 | This module is responsible for creating audit logs correctly and storing them in the database. 4 | -------------------------------------------------------------------------------- /packages/ui/src/hooks/select/README.md: -------------------------------------------------------------------------------- 1 | **Credits: These hooks are by [Roman Veselý](https://codesandbox.io/s/spectrum-multiselect-9be866-9be866?file=/src/App.tsx)** 2 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220220123028_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ALTER COLUMN "pairedUnitTemplate" SET DEFAULT E'1A-{callsign1}'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220227101539_radiochannelid_combined/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "CombinedLeoUnit" ADD COLUMN "radioChannelId" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220301110033_call_via_dispatch/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Call911" ADD COLUMN "viaDispatch" BOOLEAN DEFAULT false; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220301182948_admin_citizen_deletion/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'ALLOW_CITIZEN_DELETION_BY_NON_ADMIN'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220419144817_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "UserSoundSettings" ADD COLUMN "statusUpdate" BOOLEAN NOT NULL DEFAULT false; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220420114151_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "UserSoundSettings" ADD COLUMN "incomingCall" BOOLEAN NOT NULL DEFAULT false; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220420121656_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DepartmentValue" ADD COLUMN "isConfidential" BOOLEAN NOT NULL DEFAULT false; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221025142353_call_911_signal_100/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Call911" ADD COLUMN "isSignal100" BOOLEAN DEFAULT false; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221214124333_extra_fields_departmentvalue/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DepartmentValue" ADD COLUMN "extraFields" JSONB; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221231124604_case_number_template/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "caseNumberTemplate" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230118141608_nullable_executor_audit_log/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Notification" ALTER COLUMN "executorId" DROP NOT NULL; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230127171334_optional_vehicle_owner/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "RegisteredVehicle" ALTER COLUMN "citizenId" DROP NOT NULL; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230716142458_medical_record_desc_data/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MedicalRecord" ADD COLUMN "descriptionData" JSONB; 3 | -------------------------------------------------------------------------------- /apps/client/public/fonts/Assistant-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/fonts/Assistant-VariableFont_wght.ttf -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/national_speed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/public/map/smart-motorways/national_speed.png -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230324174132_default_permissions/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "AutoSetPropertiesUser" ADD COLUMN "defaultPermissions" TEXT[]; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230615113018_custom_template_department_value/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DepartmentValue" ADD COLUMN "customTemplate" TEXT; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230907142434_impounded_vehicle_description/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "ImpoundedVehicle" ADD COLUMN "descriptionData" JSONB; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231202095501_description_emergency_value/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "EmergencyVehicleValue" ADD COLUMN "description" TEXT; 3 | -------------------------------------------------------------------------------- /apps/client/src/components/account/images/full-row-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/src/components/account/images/full-row-color.png -------------------------------------------------------------------------------- /apps/client/src/components/account/images/row-dot-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SnailyCAD/snaily-cadv4/HEAD/apps/client/src/components/account/images/row-dot-color.png -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220305071718_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "CombinedLeoUnit" ADD COLUMN "callsign2" TEXT, 3 | ADD COLUMN "incremental" INTEGER; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230414111403_user_developer_mode/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" ADD COLUMN "developerMode" BOOLEAN NOT NULL DEFAULT false; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230517120247_medical_records_citizen_manageable/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'MEDICAL_RECORDS_CITIZEN_MANAGEABLE'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231110181854_extra_fields_emergency_vehicle/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "EmergencyVehicleValue" ADD COLUMN "extraFields" JSONB; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /apps/client/public/map/smart-motorways/CREDITS.txt: -------------------------------------------------------------------------------- 1 | # London Studios 2 | 3 | All credits to London Studios (https://londonstudios.net) for the default SmartMotorways images/signs. -------------------------------------------------------------------------------- /apps/client/src/lib/classNames.ts: -------------------------------------------------------------------------------- 1 | export function classNames(...classes: (string | number | boolean | null | undefined)[]) { 2 | return classes.filter(Boolean).join(" "); 3 | } 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "tailwindCSS.experimental.classRegex": [["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]] 4 | } 5 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211103063708_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MedicalRecord" ALTER COLUMN "type" DROP NOT NULL, 3 | ALTER COLUMN "description" DROP NOT NULL; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221011142755_is_primary_penalcode_1139/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "PenalCode" ADD COLUMN "isPrimary" BOOLEAN NOT NULL DEFAULT true; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230117055125_last_sync_inactivity/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "lastInactivitySyncTimestamp" TIMESTAMP(3); 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230705071011_multiple_departments_units_per_user/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'ALLOW_MULTIPLE_UNITS_DEPARTMENTS_PER_USER'; 3 | -------------------------------------------------------------------------------- /packages/config/README.md: -------------------------------------------------------------------------------- 1 | # @snailycad/config 2 | 3 | This is a module that holds some common used configs used in the code, such as `SocketEvents`, `Cookie`, and other constants. 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220222175328_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "call911WebhookId" TEXT, 3 | ADD COLUMN "statusesWebhookId" TEXT; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220419145730_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "boloWebhookId" TEXT, 3 | ADD COLUMN "panicButtonWebhookId" TEXT; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230110152253_last_seen_on_user/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" ADD COLUMN "lastSeen" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Dependabot for GitHub Actions 2 | version: 2 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: "/" 6 | schedule: 7 | interval: monthly 8 | -------------------------------------------------------------------------------- /apps/client/src/lib/i18n/getNextI18nConfig.ts: -------------------------------------------------------------------------------- 1 | export async function getNextI18nConfig() { 2 | const nextConfig = (await import("../../../i18n.config.mjs")).i18n; 3 | return nextConfig; 4 | } 5 | -------------------------------------------------------------------------------- /packages/utils/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | 3 | export default defineConfig({ 4 | test: { 5 | testTimeout: 10_000, // 10 seconds 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "Prisma.prisma", 4 | "esbenp.prettier-vscode", 5 | "dbaeumer.vscode-eslint", 6 | "bradlc.vscode-tailwindcss" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211214110448_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'DISCORD_AUTH'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "User" ADD COLUMN "discordId" TEXT; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220108103934_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "authScreenBgImageId" TEXT, 3 | ADD COLUMN "authScreenHeaderImageId" TEXT; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221122155409_manage_awards_qualifications_permissions/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "ToAddDefaultPermissionsKey" ADD VALUE 'MANAGE_AWARDS_AND_QUALIFICATIONS'; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220310152403_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "TaxiCall" ADD COLUMN "name" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "TowCall" ADD COLUMN "name" TEXT; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221207131235_active_dispatchers_activity_timeout/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "activeDispatchersInactivityTimeout" INTEGER; 3 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220403064022_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "maxAssignmentsToCalls" INTEGER, 3 | ADD COLUMN "maxAssignmentsToIncidents" INTEGER; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221222094403_missing_citizens/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Citizen" ADD COLUMN "dateOfMissing" TIMESTAMP(3), 3 | ADD COLUMN "missing" BOOLEAN DEFAULT false; 4 | -------------------------------------------------------------------------------- /packages/schemas/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "moduleResolution": "Bundler", 5 | "module": "ESNext", 6 | "noEmit": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "moduleResolution": "Bundler", 5 | "module": "ESNext", 6 | "noEmit": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "moduleResolution": "Bundler", 5 | "module": "ESNext", 6 | "noEmit": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220428161153_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "EmsFdDeputy" ADD COLUMN "position" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Officer" ADD COLUMN "position" TEXT; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220903095803_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "activeWarrantsInactivityTimeout" INTEGER, 3 | ADD COLUMN "boloInactivityTimeout" INTEGER; 4 | -------------------------------------------------------------------------------- /packages/audit-logger/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { AuditLogActions } from "./types/actions"; 2 | 3 | export type { AuditLogActions }; 4 | export * from "./types/action-types"; 5 | export * from "./types/actions"; 6 | -------------------------------------------------------------------------------- /packages/audit-logger/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "moduleResolution": "Bundler", 5 | "module": "ESNext", 6 | "noEmit": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/permissions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "moduleResolution": "Bundler", 5 | "module": "ESNext", 6 | "noEmit": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220407074655_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'DMV'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "RegisteredVehicle" ADD COLUMN "dmvStatus" "WhitelistStatus"; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211230101100_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'SOCIAL_SECURITY_NUMBERS'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Citizen" ADD COLUMN "socialSecurityNumber" TEXT; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220110184116_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Business" ADD COLUMN "postal" VARCHAR(255); 3 | 4 | -- AlterTable 5 | ALTER TABLE "Citizen" ADD COLUMN "postal" VARCHAR(255); 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220220115325_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "pairedUnitTemplate" TEXT DEFAULT E'1A-{callsign2}', 3 | ALTER COLUMN "pairedUnitSymbol" DROP NOT NULL; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220715065900_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Record" ALTER COLUMN "officerId" DROP NOT NULL; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Warrant" ALTER COLUMN "officerId" DROP NOT NULL; 6 | -------------------------------------------------------------------------------- /packages/ui/src/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body[data-theme="dark"] { 6 | background-color: theme("colors.primary"); 7 | color: theme("colors.white"); 8 | } 9 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220123090306_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Citizen" ALTER COLUMN "userId" DROP NOT NULL; 3 | 4 | -- AlterTable 5 | ALTER TABLE "MedicalRecord" ALTER COLUMN "userId" DROP NOT NULL; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220503141835_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "EmsFdDeputy" ADD COLUMN "incremental" INTEGER; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Officer" ADD COLUMN "incremental" INTEGER; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220829154218_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "PaymentStatus" AS ENUM ('PAID', 'UNPAID'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "Record" ADD COLUMN "paymentStatus" "PaymentStatus"; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220409062923_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "UserSoundSettings" ADD COLUMN "addedToCall" BOOLEAN NOT NULL DEFAULT false, 3 | ADD COLUMN "stopRoleplay" BOOLEAN NOT NULL DEFAULT false; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220427114851_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "QualificationValue" ADD COLUMN "description" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Value" ADD COLUMN "officerRankImageId" TEXT; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230123172736_call_911_approval/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'CALL_911_APPROVAL'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Call911" ADD COLUMN "status" "WhitelistStatus"; 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | yarn-error.log 4 | dist 5 | .env 6 | .data 7 | 8 | apps/api/public/**/*.png 9 | apps/api/public/**/*.svg 10 | apps/api/public/**/*.jpg 11 | apps/api/public/**/*.jpeg 12 | 13 | next-env.d.ts -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221208105820_bureau_of_firearms/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'BUREAU_OF_FIREARMS'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Weapon" ADD COLUMN "bofStatus" "WhitelistStatus"; 6 | -------------------------------------------------------------------------------- /apps/client/src/components/shared/Table.tsx: -------------------------------------------------------------------------------- 1 | export * from "./table/table"; 2 | export * from "./table/indeterminate-checkbox"; 3 | export * from "hooks/shared/table/use-table-state"; 4 | export * from "hooks/shared/table/use-async-table"; 5 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220502143247_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "CombinedLeoUnit" ADD COLUMN "pairedUnitTemplate" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "DivisionValue" ADD COLUMN "pairedUnitTemplate" TEXT; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220504153820_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'CITIZEN_RECORD_APPROVAL'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Record" ADD COLUMN "status" "WhitelistStatus" DEFAULT E'ACCEPTED'; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221005123120_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "PenalCodeType" AS ENUM ('INFRACTION', 'MISDEMEANOR', 'FELONY'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "PenalCode" ADD COLUMN "type" "PenalCodeType"; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230806071701_vehicle_model_images/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "RegisteredVehicle" ADD COLUMN "imageId" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "VehicleValue" ADD COLUMN "imageId" TEXT; 6 | -------------------------------------------------------------------------------- /apps/client/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/client/src/types/dnd-actions.ts: -------------------------------------------------------------------------------- 1 | export enum DndActions { 2 | MoveUnitTo911CallOrIncident = "MoveUnitTo911Call", 3 | UnassignUnitFrom911Call = "UnassignUnitFrom911Call", 4 | UnassignUnitFromIncident = "UnassignUnitFromIncident", 5 | } 6 | -------------------------------------------------------------------------------- /apps/api/.npm-upgrade.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": { 3 | "nanoid": { 4 | "versions": ">4.0.0", 5 | "reason": "" 6 | }, 7 | "is-ip": { 8 | "versions": "5.0.1", 9 | "reason": "esm" 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220125150836_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "ValueLicenseType" AS ENUM ('LICENSE', 'REGISTRATION_STATUS'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "Value" ADD COLUMN "licenseType" "ValueLicenseType"; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220514053844_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "JailTimeScale" AS ENUM ('HOURS', 'MINUTES', 'SECONDS'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "MiscCadSettings" ADD COLUMN "jailTimeScale" "JailTimeScale"; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230330144749_change_business_owner/migration.sql: -------------------------------------------------------------------------------- 1 | 2 | -- DropForeignKey 3 | ALTER TABLE "Business" DROP CONSTRAINT "Business_citizenId_fkey"; 4 | 5 | -- AlterTable 6 | ALTER TABLE "Business" DROP COLUMN "citizenId"; 7 | -------------------------------------------------------------------------------- /packages/config/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "include": ["src/**/*"], 4 | "compilerOptions": { 5 | "moduleResolution": "Bundler", 6 | "module": "ESNext", 7 | "noEmit": true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220107123346_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Business" ADD COLUMN "status" "WhitelistStatus"; 3 | 4 | -- AlterTable 5 | ALTER TABLE "cad" ADD COLUMN "businessWhitelisted" BOOLEAN NOT NULL DEFAULT false; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220916160317_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'WARRANT_STATUS_APPROVAL'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Warrant" ADD COLUMN "approvalStatus" "WhitelistStatus" DEFAULT 'ACCEPTED'; 6 | -------------------------------------------------------------------------------- /packages/utils/tests/version.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { getCADVersion } from "../src/version"; 3 | 4 | test("Should return the CAD version", async () => { 5 | expect(await getCADVersion()).toBeTypeOf("object"); 6 | }); 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: General Support (SnailyCAD Community) 3 | url: https://discord.gg/eGnrPqEH7U 4 | about: Questions about installation process and general questions should be sent via Discord if possible 5 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230330175639_employee_permissions/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Employee" ADD COLUMN "canManageEmployees" BOOLEAN NOT NULL DEFAULT false, 3 | ADD COLUMN "canManageVehicles" BOOLEAN NOT NULL DEFAULT false; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230706120138_signal_100_repeat_settings/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "signal100RepeatAmount" INTEGER DEFAULT 1, 3 | ADD COLUMN "signal100RepeatIntervalMs" INTEGER DEFAULT 1000; 4 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220817064415_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "call911InactivityTimeout" INTEGER, 3 | ADD COLUMN "incidentInactivityTimeout" INTEGER, 4 | ADD COLUMN "unitInactivityTimeout" INTEGER; 5 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230615115745_user_defined_callsign_units/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "EmsFdDeputy" ADD COLUMN "userDefinedCallsign" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Officer" ADD COLUMN "userDefinedCallsign" TEXT; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230925150249_extended_ems_fd_incidents/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "EmsFdIncident" ADD COLUMN "address" TEXT, 3 | ADD COLUMN "fireType" TEXT, 4 | ADD COLUMN "vehicleInvolved" BOOLEAN NOT NULL DEFAULT false; 5 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230821091842_comments_on_courthouse_requests/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "ExpungementRequest" ADD COLUMN "description" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "NameChangeRequest" ADD COLUMN "description" TEXT; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231223155548_draft_records/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "PublishStatus" AS ENUM ('DRAFT', 'PUBLISHED'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "Record" ADD COLUMN "publishStatus" "PublishStatus" NOT NULL DEFAULT 'DRAFT'; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211102101051_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "StatusViewMode" AS ENUM ('FULL_ROW_COLOR', 'DOT_COLOR'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "User" ADD COLUMN "statusViewMode" "StatusViewMode" NOT NULL DEFAULT E'DOT_COLOR'; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211105062536_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "StatusValueType" AS ENUM ('STATUS_CODE', 'SITUATION_CODE'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "StatusValue" ADD COLUMN "type" "StatusValueType" NOT NULL DEFAULT E'STATUS_CODE'; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220205123934_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "TableActionsAlignment" AS ENUM ('NONE', 'LEFT', 'RIGHT'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "User" ADD COLUMN "tableActionsAlignment" "TableActionsAlignment" NOT NULL DEFAULT E'RIGHT'; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230415083206_remove_old_permissions_system/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" DROP COLUMN "isDispatch", 3 | DROP COLUMN "isEmsFd", 4 | DROP COLUMN "isLeo", 5 | DROP COLUMN "isSupervisor", 6 | DROP COLUMN "isTaxi", 7 | DROP COLUMN "isTow"; 8 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "packages": [ 4 | "packages/config", 5 | "packages/permissions", 6 | "packages/types", 7 | "packages/utils", 8 | "packages/ui" 9 | ], 10 | "version": "1.74.0" 11 | } 12 | -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | test_patterns = ["**/tests/*.test.*"] 4 | 5 | [[analyzers]] 6 | name = "test-coverage" 7 | 8 | [[analyzers]] 9 | name = "docker" 10 | 11 | [[analyzers]] 12 | name = "javascript" 13 | 14 | [analyzers.meta] 15 | plugins = ["react"] 16 | -------------------------------------------------------------------------------- /apps/README.md: -------------------------------------------------------------------------------- 1 | # `apps` directory 2 | 3 | This directory holds all the application state and UI 4 | 5 | - `api/`: This directory powers SnailyCADv4's API. This processes all the incoming requests. 6 | - `client/`: This is what you as the end user will see on the screen in the browser. 7 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211219152524_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Call911" ADD COLUMN "postal" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "TaxiCall" ADD COLUMN "postal" TEXT; 6 | 7 | -- AlterTable 8 | ALTER TABLE "TowCall" ADD COLUMN "postal" TEXT; 9 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211223170541_/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the column `position` on the `StatusValue` table. All the data in the column will be lost. 5 | 6 | */ 7 | -- AlterTable 8 | ALTER TABLE "StatusValue" DROP COLUMN "position"; 9 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230531172206_remove_deprecated_features_2/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "cad" DROP COLUMN "disabledFeatures", 3 | DROP COLUMN "discordWebhookURL", 4 | DROP COLUMN "liveMapSocketURl", 5 | DROP COLUMN "logoBlurData", 6 | DROP COLUMN "maxPlateLength"; 7 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "moduleResolution": "Bundler", 5 | "module": "ESNext", 6 | "noEmit": true, 7 | "jsx": "react-jsx", 8 | "lib": ["ES2022", "DOM", "DOM.Iterable"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220427153022_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "QualificationValueType" AS ENUM ('QUALIFICATION', 'AWARD'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "QualificationValue" ADD COLUMN "qualificationType" "QualificationValueType" NOT NULL DEFAULT E'QUALIFICATION'; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220222181405_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'RADIO_CHANNEL_MANAGEMENT'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "EmsFdDeputy" ADD COLUMN "radioChannelId" TEXT; 6 | 7 | -- AlterTable 8 | ALTER TABLE "Officer" ADD COLUMN "radioChannelId" TEXT; 9 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230129085118_license_number_template/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MiscCadSettings" ADD COLUMN "driversLicenseTemplate" TEXT, 3 | ADD COLUMN "pilotLicenseTemplate" TEXT, 4 | ADD COLUMN "waterLicenseTemplate" TEXT, 5 | ADD COLUMN "weaponLicenseTemplate" TEXT; 6 | -------------------------------------------------------------------------------- /apps/client/i18n.config.mjs: -------------------------------------------------------------------------------- 1 | export const i18n = { 2 | locales: [ 3 | "en", 4 | "en-gb", 5 | "ru", 6 | "cn", 7 | "tc", 8 | "fr-FR", 9 | "de-DE", 10 | "pt-BR", 11 | "cs-CZ", 12 | "nl-BE", 13 | "zh-CN", 14 | "sv", 15 | ], 16 | defaultLocale: "en", 17 | }; 18 | -------------------------------------------------------------------------------- /apps/client/src/styles/nprogress.css: -------------------------------------------------------------------------------- 1 | /* Make clicks pass-through */ 2 | #nprogress { 3 | pointer-events: none; 4 | } 5 | 6 | #nprogress .bar { 7 | background: #778da9; 8 | 9 | position: fixed; 10 | z-index: 1031; 11 | top: 0; 12 | left: 0; 13 | 14 | width: 100%; 15 | height: 2.5px; 16 | } 17 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211109183706_/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "Officer" DROP CONSTRAINT "Officer_citizenId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "Officer" ADD CONSTRAINT "Officer_citizenId_fkey" FOREIGN KEY ("citizenId") REFERENCES "Citizen"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220811154606_/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "DLExam" DROP CONSTRAINT "DLExam_citizenId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "DLExam" ADD CONSTRAINT "DLExam_citizenId_fkey" FOREIGN KEY ("citizenId") REFERENCES "Citizen"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /packages/schemas/src/admin/values.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const CREATE_PENAL_CODE_GROUP_SCHEMA = z.object({ 4 | name: z.string().min(2).max(255), 5 | }); 6 | 7 | export const BULK_DELETE_SCHEMA = z 8 | .object({ 9 | all: z.boolean(), 10 | }) 11 | .or(z.array(z.string().min(1))); 12 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220307173213_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Citizen" ADD COLUMN "waterLicenseId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "Citizen" ADD CONSTRAINT "Citizen_waterLicenseId_fkey" FOREIGN KEY ("waterLicenseId") REFERENCES "Value"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231017072316_pet_cascade_citizen/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "Pet" DROP CONSTRAINT "Pet_citizenId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "Pet" ADD CONSTRAINT "Pet_citizenId_fkey" FOREIGN KEY ("citizenId") REFERENCES "Citizen"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # See http://EditorConfig.org for more information 2 | 3 | root = true 4 | 5 | # Every File 6 | [*] 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | charset = utf-8 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220212080210_/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "SeizedItem" DROP CONSTRAINT "SeizedItem_recordId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "SeizedItem" ADD CONSTRAINT "SeizedItem_recordId_fkey" FOREIGN KEY ("recordId") REFERENCES "Record"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220217150844_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Call911" ADD COLUMN "situationCodeId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "Call911" ADD CONSTRAINT "Call911_situationCodeId_fkey" FOREIGN KEY ("situationCodeId") REFERENCES "StatusValue"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220211155937_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DiscordRoles" ADD COLUMN "adminRoleId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "DiscordRoles" ADD CONSTRAINT "DiscordRoles_adminRoleId_fkey" FOREIGN KEY ("adminRoleId") REFERENCES "DiscordRole"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220706072729_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "CustomRole" ADD COLUMN "discordRoleId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "CustomRole" ADD CONSTRAINT "CustomRole_discordRoleId_fkey" FOREIGN KEY ("discordRoleId") REFERENCES "DiscordRole"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220819063425_/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "WeaponExam" DROP CONSTRAINT "WeaponExam_citizenId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "WeaponExam" ADD CONSTRAINT "WeaponExam_citizenId_fkey" FOREIGN KEY ("citizenId") REFERENCES "Citizen"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211102163503_/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "Call911Event" DROP CONSTRAINT "Call911Event_call911Id_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "Call911Event" ADD CONSTRAINT "Call911Event_call911Id_fkey" FOREIGN KEY ("call911Id") REFERENCES "Call911"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220622063211_/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "ApiTokenLog" DROP CONSTRAINT "ApiTokenLog_apiTokenId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "ApiTokenLog" ADD CONSTRAINT "ApiTokenLog_apiTokenId_fkey" FOREIGN KEY ("apiTokenId") REFERENCES "ApiToken"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221217072904_records_court_entry/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Record" ADD COLUMN "CourtEntryId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "Record" ADD CONSTRAINT "Record_CourtEntryId_fkey" FOREIGN KEY ("CourtEntryId") REFERENCES "CourtEntry"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230927070429_vehicle_speed_record/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "VehiclePaceType" AS ENUM ('PACE', 'RADAR', 'LASER', 'OTHER'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "Record" ADD COLUMN "speedLimit" TEXT, 6 | ADD COLUMN "vehiclePaceType" "VehiclePaceType", 7 | ADD COLUMN "vehicleSpeed" TEXT; 8 | -------------------------------------------------------------------------------- /packages/schemas/src/truck-log.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const CREATE_TRUCK_LOG_SCHEMA = z.object({ 4 | endedAt: z.string().min(1).max(255), 5 | startedAt: z.string().min(1).max(255), 6 | citizenId: z.string().min(2).max(255), 7 | vehicleId: z.string().min(2).max(255), 8 | notes: z.string().nullish(), 9 | }); 10 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220224171140_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DepartmentValue" ADD COLUMN "statusValueId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "DepartmentValue" ADD CONSTRAINT "DepartmentValue_statusValueId_fkey" FOREIGN KEY ("statusValueId") REFERENCES "StatusValue"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220430124313_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "CombinedLeoUnit" ADD COLUMN "departmentId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "CombinedLeoUnit" ADD CONSTRAINT "CombinedLeoUnit_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "DepartmentValue"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220801060848_/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "ActiveDispatchers" DROP CONSTRAINT "ActiveDispatchers_userId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "ActiveDispatchers" ADD CONSTRAINT "ActiveDispatchers_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /packages/utils/src/api-url.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * get the api url from the environment variables 3 | */ 4 | export function getAPIUrl(): string { 5 | const envUrl = process.env.NEXT_PUBLIC_PROD_ORIGIN ?? "http://localhost:8080/v1"; 6 | 7 | if (envUrl.endsWith("/v1")) { 8 | return envUrl; 9 | } 10 | 11 | return `${envUrl}/v1`; 12 | } 13 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220219064214_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DiscordRoles" ADD COLUMN "whitelistedRoleId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "DiscordRoles" ADD CONSTRAINT "DiscordRoles_whitelistedRoleId_fkey" FOREIGN KEY ("whitelistedRoleId") REFERENCES "DiscordRole"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /packages/config/src/routes.ts: -------------------------------------------------------------------------------- 1 | export type Method = "GET" | "POST" | "PATCH" | "PUT" | "DELETE"; 2 | 3 | /** 4 | * `*` = all methods 5 | */ 6 | export type DisabledRoute = [string, Method[] | "*"]; 7 | 8 | export const DISABLED_API_TOKEN_ROUTES: DisabledRoute[] = [ 9 | ["/v1/user", "*"], 10 | ["/v1/admin/manage/cad-settings", "*"], 11 | ]; 12 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221019113803_auto_revoke_suspended_licenses/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "SuspendedCitizenLicenses" ADD COLUMN "driverLicenseTimeEnd" TIMESTAMP(3), 3 | ADD COLUMN "firearmsLicenseTimeEnd" TIMESTAMP(3), 4 | ADD COLUMN "pilotLicenseTimeEnd" TIMESTAMP(3), 5 | ADD COLUMN "waterLicenseTimeEnd" TIMESTAMP(3); 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220228062318_situation_code_incident/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "LeoIncident" ADD COLUMN "situationCodeId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "LeoIncident" ADD CONSTRAINT "LeoIncident_situationCodeId_fkey" FOREIGN KEY ("situationCodeId") REFERENCES "StatusValue"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220413130107_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DepartmentValue" ADD COLUMN "defaultOfficerRankId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "DepartmentValue" ADD CONSTRAINT "DepartmentValue_defaultOfficerRankId_fkey" FOREIGN KEY ("defaultOfficerRankId") REFERENCES "Value"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/client/src/styles/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "Assistant"; 3 | src: url("../../public/fonts/Assistant-VariableFont_wght.ttf"); 4 | font-weight: 100 1000; 5 | font-stretch: 25% 151%; 6 | font-display: swap; 7 | } 8 | 9 | html, 10 | body, 11 | #map-calls, 12 | .leaflet-container { 13 | font-family: "Assistant", sans-serif; 14 | } 15 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221028153837_impound_lot_officer_impounded_by/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "ImpoundedVehicle" ADD COLUMN "officerId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "ImpoundedVehicle" ADD CONSTRAINT "ImpoundedVehicle_officerId_fkey" FOREIGN KEY ("officerId") REFERENCES "Officer"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230221062301_business_records/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Record" ADD COLUMN "businessId" TEXT, 3 | ALTER COLUMN "citizenId" DROP NOT NULL; 4 | 5 | -- AddForeignKey 6 | ALTER TABLE "Record" ADD CONSTRAINT "Record_businessId_fkey" FOREIGN KEY ("businessId") REFERENCES "Business"("id") ON DELETE SET NULL ON UPDATE CASCADE; 7 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230412175911_custom_combined_unit_callsign/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'USER_DEFINED_CALLSIGN_COMBINED_UNIT'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "CombinedEmsFdUnit" ADD COLUMN "userDefinedCallsign" TEXT; 6 | 7 | -- AlterTable 8 | ALTER TABLE "CombinedLeoUnit" ADD COLUMN "userDefinedCallsign" TEXT; 9 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220505141915_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "CombinedLeoUnit" ADD COLUMN "lastStatusChangeTimestamp" TIMESTAMP(3); 3 | 4 | -- AlterTable 5 | ALTER TABLE "EmsFdDeputy" ADD COLUMN "lastStatusChangeTimestamp" TIMESTAMP(3); 6 | 7 | -- AlterTable 8 | ALTER TABLE "Officer" ADD COLUMN "lastStatusChangeTimestamp" TIMESTAMP(3); 9 | -------------------------------------------------------------------------------- /apps/api/src/lib/auth/post-auth.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "lib/data/prisma"; 2 | 3 | export interface PostLoginFlowOptions { 4 | userId: string; 5 | } 6 | 7 | export async function postLoginFlowHandler(options: PostLoginFlowOptions) { 8 | await prisma.user.update({ 9 | where: { id: options.userId }, 10 | data: { lastSeen: new Date() }, 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211101093935_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "MedicalRecord" ADD COLUMN "bloodGroupId" TEXT, 3 | ALTER COLUMN "type" SET DATA TYPE TEXT; 4 | 5 | -- AddForeignKey 6 | ALTER TABLE "MedicalRecord" ADD CONSTRAINT "MedicalRecord_bloodGroupId_fkey" FOREIGN KEY ("bloodGroupId") REFERENCES "Value"("id") ON DELETE SET NULL ON UPDATE CASCADE; 7 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220424090603_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'USER_API_TOKENS'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "User" ADD COLUMN "apiTokenId" TEXT; 6 | 7 | -- AddForeignKey 8 | ALTER TABLE "User" ADD CONSTRAINT "User_apiTokenId_fkey" FOREIGN KEY ("apiTokenId") REFERENCES "ApiToken"("id") ON DELETE SET NULL ON UPDATE CASCADE; 9 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220521060146_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "OfficerLog" ADD COLUMN "emsFdDeputyId" TEXT, 3 | ALTER COLUMN "officerId" DROP NOT NULL; 4 | 5 | -- AddForeignKey 6 | ALTER TABLE "OfficerLog" ADD CONSTRAINT "OfficerLog_emsFdDeputyId_fkey" FOREIGN KEY ("emsFdDeputyId") REFERENCES "EmsFdDeputy"("id") ON DELETE CASCADE ON UPDATE CASCADE; 7 | -------------------------------------------------------------------------------- /apps/client/src/components/dispatch/map/smart-signs/render-map-smart-signs.tsx: -------------------------------------------------------------------------------- 1 | import { SmartSignsMarker } from "./smart-sign-marker"; 2 | import { useSmartSigns } from "./use-smart-signs"; 3 | 4 | export function RenderMapSmartSigns() { 5 | const smartSigns = useSmartSigns(); 6 | return smartSigns.map((marker) => ); 7 | } 8 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230225102544_business_records_logs/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "RecordLog" ADD COLUMN "businessId" TEXT, 3 | ALTER COLUMN "citizenId" DROP NOT NULL; 4 | 5 | -- AddForeignKey 6 | ALTER TABLE "RecordLog" ADD CONSTRAINT "RecordLog_businessId_fkey" FOREIGN KEY ("businessId") REFERENCES "Business"("id") ON DELETE SET NULL ON UPDATE CASCADE; 7 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230306153731_dispatch_department_filter/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "ActiveDispatchers" ADD COLUMN "departmentId" TEXT; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "ActiveDispatchers" ADD CONSTRAINT "ActiveDispatchers_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "DepartmentValue"("id") ON DELETE SET NULL ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /packages/schemas/src/admin/import/weapons.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const WEAPON_SCHEMA = z.object({ 4 | modelId: z.string().min(2).max(255), 5 | ownerId: z.string().min(2).max(255), 6 | registrationStatusId: z.string().min(2).max(255), 7 | userId: z.string().nullish(), 8 | }); 9 | 10 | export const WEAPON_SCHEMA_ARR = z.array(WEAPON_SCHEMA).min(1); 11 | -------------------------------------------------------------------------------- /packages/permissions/src/defaults/citizen.ts: -------------------------------------------------------------------------------- 1 | import { Permissions } from "../permissions"; 2 | 3 | export const defaultTowPermissions = [ 4 | Permissions.ViewTowCalls, 5 | Permissions.ManageTowCalls, 6 | Permissions.ViewTowLogs, 7 | Permissions.CreateBusinesses, 8 | ]; 9 | 10 | export const defaultTaxiPermissions = [Permissions.ViewTaxiCalls, Permissions.ManageTaxiCalls]; 11 | -------------------------------------------------------------------------------- /apps/api/src/utils/findFirstSmallestInArray.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * find the first smallest missing item from an array 3 | */ 4 | export function findFirstSmallestInArray(numbers: number[]) { 5 | const sorted = numbers.sort((a, b) => a - b); 6 | let int = 1; 7 | 8 | for (let i = 0; i < sorted.length; i++) { 9 | if (sorted[i] === int) int++; 10 | } 11 | 12 | return int; 13 | } 14 | -------------------------------------------------------------------------------- /apps/client/src/hooks/global/components/socket-error-component.tsx: -------------------------------------------------------------------------------- 1 | import { Alert } from "@snailycad/ui"; 2 | import { useTranslations } from "use-intl"; 3 | 4 | export function SocketErrorComponent() { 5 | const t = useTranslations("Errors"); 6 | 7 | return ( 8 | 9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /apps/api/scripts/postbuild.mjs: -------------------------------------------------------------------------------- 1 | import { cp } from "node:fs/promises"; 2 | import { resolve } from "node:path"; 3 | 4 | const currentDir = process.cwd(); 5 | const templatesDir = resolve(currentDir, "src/templates"); 6 | 7 | // The templates directory isn't copied over by SWC. Therefore, we have to do this manually. 8 | cp(templatesDir, resolve(currentDir, "dist/templates"), { recursive: true }); 9 | -------------------------------------------------------------------------------- /apps/client/src/pages/officer/supervisor/dl-exams.tsx: -------------------------------------------------------------------------------- 1 | import type { GetServerSideProps } from "next"; 2 | 3 | export default function Page() { 4 | return null; 5 | } 6 | 7 | export const getServerSideProps: GetServerSideProps = async () => { 8 | return { 9 | redirect: { 10 | destination: "/officer/supervisor/exams", 11 | permanent: true, 12 | }, 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/types/src/lib/cad-feature.ts: -------------------------------------------------------------------------------- 1 | import type * as Enums from "../enums"; 2 | 3 | export interface CadFeatureOptions { 4 | [Enums.Feature.LICENSE_EXAMS]: LicenseExamFeatureOption; 5 | [Enums.Feature.COURTHOUSE]: CourthouseFeatureOption; 6 | } 7 | 8 | export type LicenseExamFeatureOption = Enums.LicenseExamType[]; 9 | export type CourthouseFeatureOption = Enums.CourthouseType[]; 10 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220306164233_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "VehicleInspectionStatus" AS ENUM ('PASSED', 'FAILED'); 3 | 4 | -- CreateEnum 5 | CREATE TYPE "VehicleTaxStatus" AS ENUM ('TAXED', 'UNTAXED'); 6 | 7 | -- AlterTable 8 | ALTER TABLE "RegisteredVehicle" ADD COLUMN "inspectionStatus" "VehicleInspectionStatus", 9 | ADD COLUMN "taxStatus" "VehicleTaxStatus"; 10 | -------------------------------------------------------------------------------- /apps/client/locales/cn/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "卡车", 4 | "createTruckLog": "创建卡车日志", 5 | "editTruckLog": "编辑卡车日志", 6 | "deleteTruckLog": "删除卡车日志", 7 | "driver": "司机", 8 | "vehicle": "车辆", 9 | "startedAt": "开始于", 10 | "endedAt": "结束于", 11 | "noTruckLogs": "你还没有卡车。", 12 | "alert_deleteTruckLog": "是否确实要删除此卡车日志?此操作无法撤消。" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/client/locales/tc/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "卡車", 4 | "createTruckLog": "創建卡車日誌", 5 | "editTruckLog": "編輯卡車日誌", 6 | "deleteTruckLog": "刪除卡車日誌", 7 | "driver": "司機", 8 | "vehicle": "車輛", 9 | "startedAt": "開始於", 10 | "endedAt": "結束於", 11 | "noTruckLogs": "你還沒有卡車。", 12 | "alert_deleteTruckLog": "是否確實要刪除此卡車日誌?此操作無法撤消。" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/utils/src/utils/prefix-number.ts: -------------------------------------------------------------------------------- 1 | export function prefixNumber(id: number, depth: number) { 2 | if (depth <= 0) return id.toString(); 3 | 4 | const stringifiedId = id.toString(); 5 | const newId = [stringifiedId]; 6 | 7 | for (let i = 0; i <= depth; i++) { 8 | if (stringifiedId.length < i) { 9 | newId.unshift("0"); 10 | } 11 | } 12 | 13 | return newId.join(""); 14 | } 15 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221108165656_cascade_impounded_vehicles/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "ImpoundedVehicle" DROP CONSTRAINT "ImpoundedVehicle_registeredVehicleId_fkey"; 3 | 4 | -- AddForeignKey 5 | ALTER TABLE "ImpoundedVehicle" ADD CONSTRAINT "ImpoundedVehicle_registeredVehicleId_fkey" FOREIGN KEY ("registeredVehicleId") REFERENCES "RegisteredVehicle"("id") ON DELETE CASCADE ON UPDATE CASCADE; 6 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | postgres: 3 | container_name: "snaily-cad-postgres" 4 | image: postgres 5 | environment: 6 | POSTGRES_PORT: ${DB_PORT} 7 | POSTGRES_USER: ${POSTGRES_USER} 8 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} 9 | POSTGRES_DB: ${POSTGRES_DB} 10 | ports: 11 | - "${DB_PORT}:5432" 12 | volumes: 13 | - ./.dev-data:/var/lib/postgresql/data 14 | -------------------------------------------------------------------------------- /packages/schemas/src/bleeter.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const BLEETER_SCHEMA = z.object({ 4 | title: z.string().min(2).max(255), 5 | body: z.string().nullish(), 6 | bodyData: z.any().nullish(), 7 | }); 8 | 9 | export const BLEETER_PROFILE_SCHEMA = z.object({ 10 | handle: z.string().min(2).max(255).toLowerCase(), 11 | name: z.string().min(2).max(255), 12 | bio: z.string().nullish(), 13 | }); 14 | -------------------------------------------------------------------------------- /packages/ui/src/components/dnd/dnd-provider.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { DndProvider as ReactDndProvider } from "react-dnd"; 3 | import { HTML5Backend } from "react-dnd-html5-backend"; 4 | 5 | interface Props { 6 | children: React.ReactNode; 7 | } 8 | 9 | export function DndProvider(props: Props) { 10 | return {props.children}; 11 | } 12 | -------------------------------------------------------------------------------- /apps/api/src/exceptions/extended-not-found.ts: -------------------------------------------------------------------------------- 1 | import type { ResponseErrorObject } from "@tsed/common"; 2 | import { NotFound } from "@tsed/exceptions"; 3 | 4 | export class ExtendedNotFound extends NotFound implements ResponseErrorObject { 5 | headers = {}; 6 | errors = {}; 7 | 8 | constructor(errors: Record, message = "notFound") { 9 | super(message); 10 | this.errors = errors; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220128083746_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "User2FA" ( 3 | "id" TEXT NOT NULL, 4 | "secret" TEXT NOT NULL, 5 | "userId" TEXT NOT NULL, 6 | 7 | CONSTRAINT "User2FA_pkey" PRIMARY KEY ("id") 8 | ); 9 | 10 | -- AddForeignKey 11 | ALTER TABLE "User2FA" ADD CONSTRAINT "User2FA_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 12 | -------------------------------------------------------------------------------- /apps/client/locales/cn/bleeter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Bleeter": { 3 | "bleeter": "布利特", 4 | "noPosts": "目前还没有更出色的帖子。稍后再回来查看", 5 | "createBleet": "创造哔哔声", 6 | "editBleet": "编辑哔哔声", 7 | "deleteBleet": "删除 Bleet", 8 | "viewBleet": "查看漂流", 9 | "headerImage": "页眉图像", 10 | "bleetTitle": "曦曦标题", 11 | "bleetBody": "飘飘的身体", 12 | "alert_deleteBleet": "是否确实要删除\"{title}\"?此操作无法撤消." 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/client/locales/tc/bleeter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Bleeter": { 3 | "bleeter": "布利特", 4 | "noPosts": "目前還沒有更出色的帖子。稍後再回來查看", 5 | "createBleet": "創造嗶嗶聲", 6 | "editBleet": "編輯嗶嗶聲", 7 | "deleteBleet": "刪除 Bleet", 8 | "viewBleet": "查看漂流", 9 | "headerImage": "頁眉圖像", 10 | "bleetTitle": "曦曦標題", 11 | "bleetBody": "飄飄的身體", 12 | "alert_deleteBleet": "是否確實要刪除\"{title}\"?此操作無法撤消." 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230119152628_nullable_executor_audit_log/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "AuditLog" DROP CONSTRAINT "AuditLog_executorId_fkey"; 3 | 4 | -- AlterTable 5 | ALTER TABLE "AuditLog" ALTER COLUMN "executorId" DROP NOT NULL; 6 | 7 | -- AddForeignKey 8 | ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_executorId_fkey" FOREIGN KEY ("executorId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; 9 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230614145057_string_badge_number/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "EmsFdDeputy" ADD COLUMN "badgeNumberString" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Officer" ADD COLUMN "badgeNumberString" TEXT; 6 | 7 | -- Migrate all officers and deputies 8 | UPDATE "Officer" SET "badgeNumberString" = "badgeNumber"::TEXT; 9 | 10 | UPDATE "EmsFdDeputy" SET "badgeNumberString" = "badgeNumber"::TEXT; -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220408062540_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DiscordRoles" ADD COLUMN "adminRolePermissions" TEXT[], 3 | ADD COLUMN "dispatchRolePermissions" TEXT[], 4 | ADD COLUMN "emsFdRolePermissions" TEXT[], 5 | ADD COLUMN "leoRolePermissions" TEXT[], 6 | ADD COLUMN "leoSupervisorRolePermissions" TEXT[], 7 | ADD COLUMN "taxiRolePermissions" TEXT[], 8 | ADD COLUMN "towRolePermissions" TEXT[]; 9 | -------------------------------------------------------------------------------- /apps/client/locales/zh-CN/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "卡车日志", 4 | "createTruckLog": "创建卡车日志", 5 | "editTruckLog": "编辑卡车日志", 6 | "deleteTruckLog": "删除卡车日志", 7 | "driver": "司机", 8 | "vehicle": "车辆", 9 | "startedAt": "开始时间", 10 | "endedAt": "结束时间", 11 | "noTruckLogs": "您还没有任何卡车日志。", 12 | "notes": "备注", 13 | "alert_deleteTruckLog": "您确定要删除此卡车日志吗?此操作无法撤销。" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/ui/src/components/stories/helpers/loader.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { Loader } from "../../loader"; 3 | 4 | const meta = { 5 | title: "Helpers/Loader", 6 | component: Loader, 7 | tags: ["autodocs"], 8 | } satisfies Meta; 9 | 10 | export default meta; 11 | type Story = StoryObj; 12 | 13 | export const Default: Story = { 14 | args: {}, 15 | }; 16 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230531171901_remove_deprecated_features/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "Officer" DROP CONSTRAINT "Officer_divisionId_fkey"; 3 | 4 | -- AlterTable 5 | ALTER TABLE "MiscCadSettings" DROP COLUMN "boloWebhookId", 6 | DROP COLUMN "call911WebhookId", 7 | DROP COLUMN "panicButtonWebhookId", 8 | DROP COLUMN "statusesWebhookId"; 9 | 10 | -- AlterTable 11 | ALTER TABLE "Officer" DROP COLUMN "divisionId"; 12 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231021140807_deletable_penal_codes/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "Violation" DROP CONSTRAINT "Violation_penalCodeId_fkey"; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Violation" ALTER COLUMN "penalCodeId" DROP NOT NULL; 6 | 7 | -- AddForeignKey 8 | ALTER TABLE "Violation" ADD CONSTRAINT "Violation_penalCodeId_fkey" FOREIGN KEY ("penalCodeId") REFERENCES "PenalCode"("id") ON DELETE SET NULL ON UPDATE CASCADE; 9 | -------------------------------------------------------------------------------- /packages/schemas/src/dispatch/bolos.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const CREATE_BOLO_SCHEMA = z.object({ 4 | description: z.string().min(2), 5 | name: z.string().max(255).nullable(), 6 | plate: z.string().max(255).nullable(), 7 | color: z.string().max(255).nullable(), 8 | model: z.string().max(255).nullable(), 9 | type: z 10 | .string() 11 | .min(2) 12 | .max(255) 13 | .regex(/PERSON|VEHICLE|OTHER/), 14 | }); 15 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211229183021_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | -- This migration adds more than one value to an enum. 3 | -- With PostgreSQL versions 11 and earlier, this is not possible 4 | -- in a single migration. This can be worked around by creating 5 | -- multiple migrations, each migration adding only one value to 6 | -- the enum. 7 | 8 | 9 | ALTER TYPE "Feature" ADD VALUE 'CALLS_911'; 10 | ALTER TYPE "Feature" ADD VALUE 'WEAPON_REGISTRATION'; 11 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231023170026_disposition_code_call/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Call911" ADD COLUMN "dispositionCodeId" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "CallTypeValue" ADD COLUMN "isDisposition" BOOLEAN NOT NULL DEFAULT false; 6 | 7 | -- AddForeignKey 8 | ALTER TABLE "Call911" ADD CONSTRAINT "Call911_dispositionCodeId_fkey" FOREIGN KEY ("dispositionCodeId") REFERENCES "CallTypeValue"("id") ON DELETE SET NULL ON UPDATE CASCADE; 9 | -------------------------------------------------------------------------------- /packages/schemas/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./auth"; 2 | export * from "./citizen"; 3 | export * from "./admin/values"; 4 | export * from "./admin/values/import"; 5 | export * from "./admin"; 6 | export * from "./bleeter"; 7 | export * from "./tow"; 8 | export * from "./businesses"; 9 | export * from "./dispatch"; 10 | export * from "./leo"; 11 | export * from "./records"; 12 | export * from "./ems-fd"; 13 | export * from "./truck-log"; 14 | export * from "./court"; 15 | -------------------------------------------------------------------------------- /apps/client/tests/getTranslations.test.ts: -------------------------------------------------------------------------------- 1 | import { getTranslations } from "../src/lib/getTranslation"; 2 | import { expect, test } from "vitest"; 3 | 4 | test("Should return translations for calls & admin in EN", async () => { 5 | expect(await getTranslations(["calls", "admin"], "en")).toBeTypeOf("object"); 6 | }); 7 | 8 | test("Should return translations for citizen in RU", async () => { 9 | expect(await getTranslations(["citizen"], "ru")).toBeTypeOf("object"); 10 | }); 11 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221224074854_vehicle_records/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Record" ADD COLUMN "address" TEXT, 3 | ADD COLUMN "vehicleColor" TEXT, 4 | ADD COLUMN "vehicleId" TEXT, 5 | ADD COLUMN "vehicleModel" TEXT, 6 | ADD COLUMN "vehiclePlate" TEXT; 7 | 8 | -- AddForeignKey 9 | ALTER TABLE "Record" ADD CONSTRAINT "Record_vehicleId_fkey" FOREIGN KEY ("vehicleId") REFERENCES "RegisteredVehicle"("id") ON DELETE SET NULL ON UPDATE CASCADE; 10 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230223061954_editable_ssn_vin_toggle/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | -- This migration adds more than one value to an enum. 3 | -- With PostgreSQL versions 11 and earlier, this is not possible 4 | -- in a single migration. This can be worked around by creating 5 | -- multiple migrations, each migration adding only one value to 6 | -- the enum. 7 | 8 | 9 | ALTER TYPE "Feature" ADD VALUE 'EDITABLE_SSN'; 10 | ALTER TYPE "Feature" ADD VALUE 'EDITABLE_VIN'; 11 | -------------------------------------------------------------------------------- /packages/ui/src/context/radio-field-context.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import type { RadioGroupState } from "@react-stately/radio"; 3 | 4 | export const RadioContext = React.createContext(undefined); 5 | 6 | export function useRadioFieldContext() { 7 | const state = React.useContext(RadioContext); 8 | if (!state) { 9 | throw new Error("useRadioFieldContext must be used within a RadioGroupField"); 10 | } 11 | return state; 12 | } 13 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220509170215_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | -- This migration adds more than one value to an enum. 3 | -- With PostgreSQL versions 11 and earlier, this is not possible 4 | -- in a single migration. This can be worked around by creating 5 | -- multiple migrations, each migration adding only one value to 6 | -- the enum. 7 | 8 | 9 | ALTER TYPE "DiscordWebhookType" ADD VALUE 'CITIZEN_RECORD'; 10 | ALTER TYPE "DiscordWebhookType" ADD VALUE 'VEHICLE_IMPOUNDED'; 11 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230917161539_dashboard_layout_order/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "DashboardLayoutCardType" AS ENUM ('ACTIVE_CALLS', 'ACTIVE_BOLOS', 'ACTIVE_WARRANTS', 'ACTIVE_OFFICERS', 'ACTIVE_DEPUTIES', 'ACTIVE_INCIDENTS'); 3 | 4 | -- AlterTable 5 | ALTER TABLE "User" ADD COLUMN "dispatchLayoutOrder" "DashboardLayoutCardType"[], 6 | ADD COLUMN "emsFdLayoutOrder" "DashboardLayoutCardType"[], 7 | ADD COLUMN "officerLayoutOrder" "DashboardLayoutCardType"[]; 8 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230203153054_forced_oauth_connections/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | -- This migration adds more than one value to an enum. 3 | -- With PostgreSQL versions 11 and earlier, this is not possible 4 | -- in a single migration. This can be worked around by creating 5 | -- multiple migrations, each migration adding only one value to 6 | -- the enum. 7 | 8 | 9 | ALTER TYPE "Feature" ADD VALUE 'FORCE_DISCORD_AUTH'; 10 | ALTER TYPE "Feature" ADD VALUE 'FORCE_STEAM_AUTH'; 11 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230907153100_blacklisted_words/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "BlacklistedWord" ( 3 | "id" TEXT NOT NULL, 4 | "word" TEXT NOT NULL, 5 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 6 | "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | 8 | CONSTRAINT "BlacklistedWord_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- CreateIndex 12 | CREATE UNIQUE INDEX "BlacklistedWord_id_key" ON "BlacklistedWord"("id"); 13 | -------------------------------------------------------------------------------- /apps/client/src/components/dispatch/map/smart-motorway-signs/render-map-smart-motorway-signs.tsx: -------------------------------------------------------------------------------- 1 | import { SmartMotorwaySignsMarker } from "./smart-motorway-sign-marker"; 2 | import { useSmartMotorwaySigns } from "./use-smart-motorway-signs"; 3 | 4 | export function RenderMapSmartMotorwaySigns() { 5 | const smartMotorwaySigns = useSmartMotorwaySigns(); 6 | return smartMotorwaySigns?.map((marker, idx) => ( 7 | 8 | )); 9 | } 10 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230624061332_more_webhook_types/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | -- This migration adds more than one value to an enum. 3 | -- With PostgreSQL versions 11 and earlier, this is not possible 4 | -- in a single migration. This can be worked around by creating 5 | -- multiple migrations, each migration adding only one value to 6 | -- the enum. 7 | 8 | 9 | ALTER TYPE "DiscordWebhookType" ADD VALUE 'BLEETER_POST'; 10 | ALTER TYPE "DiscordWebhookType" ADD VALUE 'CITIZEN_DECLARED_DEAD'; 11 | -------------------------------------------------------------------------------- /apps/client/locales/cn/courthouse.json: -------------------------------------------------------------------------------- 1 | { 2 | "Courthouse": { 3 | "courthouse": "圣安德烈亚斯法院", 4 | "requestExpungement": "请求删除", 5 | "noExpungementRequests": "您没有任何消除请求", 6 | "request": "请求", 7 | "noPendingRequests": "没有待处理的清除请求", 8 | "expungementRequests": "消除请求", 9 | "nameChangeRequests": "姓名更改请求", 10 | "requestNameChange": "请求更改名称", 11 | "noNameChangeRequests": "没有名称更改请求。", 12 | "newName": "新名称", 13 | "newSurname": "新姓氏", 14 | "status": "状态" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/client/locales/tc/courthouse.json: -------------------------------------------------------------------------------- 1 | { 2 | "Courthouse": { 3 | "courthouse": "圣安德烈亚斯法院", 4 | "requestExpungement": "请求删除", 5 | "noExpungementRequests": "您没有任何消除请求", 6 | "request": "请求", 7 | "noPendingRequests": "没有待处理的清除请求", 8 | "expungementRequests": "消除请求", 9 | "nameChangeRequests": "姓名更改请求", 10 | "requestNameChange": "请求更改名称", 11 | "noNameChangeRequests": "没有名称更改请求。", 12 | "newName": "新名称", 13 | "newSurname": "新姓氏", 14 | "status": "状态" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/api/tests/smallest-int-in-array.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { findFirstSmallestInArray } from "../src/utils/findFirstSmallestInArray"; 3 | 4 | const ARRAY_1 = [1, 5, 3, 2]; 5 | const ARRAY_2 = [1, 2, 3, 4, 10, 25, 32]; 6 | 7 | test("Should return smallest int in an array", () => { 8 | expect(findFirstSmallestInArray(ARRAY_1)).toBe(4); 9 | }); 10 | 11 | test("Should return smallest int in an array", () => { 12 | expect(findFirstSmallestInArray(ARRAY_2)).toBe(5); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/client/src/components/shared/image-wrapper.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import Image, { type ImageProps } from "next/image"; 3 | 4 | interface Props extends ImageProps { 5 | fallback?: React.ReactNode; 6 | } 7 | 8 | export function ImageWrapper(props: Props) { 9 | const fallback = (props.fallback ?? null) as React.JSX.Element; 10 | const [hasError, setHasError] = React.useState(false); 11 | 12 | return hasError ? fallback : setHasError(true)} />; 13 | } 14 | -------------------------------------------------------------------------------- /apps/client/src/state/citizen/pets-state.ts: -------------------------------------------------------------------------------- 1 | import type { Pet } from "@snailycad/types"; 2 | import { shallow } from "zustand/shallow"; 3 | import { createWithEqualityFn } from "zustand/traditional"; 4 | 5 | interface PetsState { 6 | currentPet: Pet | null; 7 | setCurrentPet(pet: Pet): void; 8 | } 9 | 10 | export const usePetsState = createWithEqualityFn()( 11 | (set) => ({ 12 | currentPet: null, 13 | setCurrentPet: (pet) => set({ currentPet: pet }), 14 | }), 15 | shallow, 16 | ); 17 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Bug 6 | 7 | - [ ] Related issues linked using `closes: #number` 8 | 9 | ## Feature 10 | 11 | - [ ] Related issues linked using `closes: #number` 12 | - [ ] There is only 1 db migration with no breaking changes. 13 | 14 | ## Translations 15 | 16 | - [ ] `.json` files are formatted: `pnpm format` 17 | - [ ] Translations are correct 18 | - [ ] New translation? It's been added to `i18n.config.mjs` 19 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230609080207_multi_live_map_urls/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "LiveMapURL" ( 3 | "id" TEXT NOT NULL, 4 | "url" TEXT NOT NULL, 5 | "name" TEXT NOT NULL, 6 | "miscCadSettingsId" TEXT, 7 | 8 | CONSTRAINT "LiveMapURL_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- AddForeignKey 12 | ALTER TABLE "LiveMapURL" ADD CONSTRAINT "LiveMapURL_miscCadSettingsId_fkey" FOREIGN KEY ("miscCadSettingsId") REFERENCES "MiscCadSettings"("id") ON DELETE CASCADE ON UPDATE CASCADE; 13 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | | Version | Supported | 6 | | ----------- | ------------------ | 7 | | > 1.30.x | :white_check_mark: | 8 | | <= 1.30.x | Partial | 9 | | 0.x.x-alpha | ❌ | 10 | 11 | ## Reporting a Vulnerability 12 | 13 | If you find any vulnerabilities in the supported versions, [join our Discord server](https://discord.com/invite/eGnrPqEH7U) and head over to the `#security-vulnerabilities` channel to report them there. 14 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231023170956_leo_ems_fd_incident_webhooks/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | -- This migration adds more than one value to an enum. 3 | -- With PostgreSQL versions 11 and earlier, this is not possible 4 | -- in a single migration. This can be worked around by creating 5 | -- multiple migrations, each migration adding only one value to 6 | -- the enum. 7 | 8 | 9 | ALTER TYPE "DiscordWebhookType" ADD VALUE 'LEO_INCIDENT_CREATED'; 10 | ALTER TYPE "DiscordWebhookType" ADD VALUE 'EMS_FD_INCIDENT_CREATED'; 11 | -------------------------------------------------------------------------------- /packages/ui/src/components/stories/status/infofield.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | 3 | import { Infofield } from "../../infofield"; 4 | 5 | const meta = { 6 | title: "Status/Infofield", 7 | component: Infofield, 8 | tags: ["autodocs"], 9 | } satisfies Meta; 10 | 11 | export default meta; 12 | type Story = StoryObj; 13 | 14 | export const Default: Story = { 15 | args: { 16 | label: "Full Name", 17 | children: "John doe", 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2.feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Request a feature or a change to improve SnailyCAD 3 | labels: "enhancement" 4 | body: 5 | - type: textarea 6 | attributes: 7 | label: Describe the feature 8 | description: Describe the feature you'd like to get added. 9 | validations: 10 | required: true 11 | - type: textarea 12 | attributes: 13 | label: Additional Context 14 | description: Images, videos, etc. 15 | validations: 16 | required: false 17 | -------------------------------------------------------------------------------- /apps/api/src/exceptions/feature-not-enabled.ts: -------------------------------------------------------------------------------- 1 | import type { ResponseErrorObject } from "@tsed/common"; 2 | import { BadRequest } from "@tsed/exceptions"; 3 | import type { IsFeatureEnabledOptions } from "middlewares/is-enabled"; 4 | 5 | export class FeatureNotEnabled extends BadRequest implements ResponseErrorObject { 6 | headers = {}; 7 | errors = {}; 8 | 9 | constructor(options: IsFeatureEnabledOptions) { 10 | super("featureNotEnabled"); 11 | this.errors = { message: "featureNotEnabled", data: options }; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/api/src/lib/discord/performDiscordRequest.ts: -------------------------------------------------------------------------------- 1 | import type { REST } from "@discordjs/rest"; 2 | import { getRest } from "./config"; 3 | 4 | interface Options { 5 | onError?(error: unknown): void; 6 | handler(rest: REST): unknown; 7 | } 8 | 9 | export async function performDiscordRequest(options: Options): Promise { 10 | try { 11 | const rest = getRest(); 12 | return (await options.handler(rest)) as T | null; 13 | } catch (error) { 14 | options.onError?.(error); 15 | return null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/client/locales/cs-CZ/bleeter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Bleeter": { 3 | "bleeter": "Twitter", 4 | "noPosts": "Zatím zde nejsou žádné příspěvky.", 5 | "createBleet": "Vytvořit Tweet", 6 | "editBleet": "Uprav Tweet", 7 | "deleteBleet": "Smaž Tweet", 8 | "viewBleet": "Koukni se Tweet", 9 | "headerImage": "Uvodni obrazek", 10 | "bleetTitle": "Tweet titulek", 11 | "bleetBody": "Tweet obsah", 12 | "alert_deleteBleet": "Opravdu to chces smazat \"{title}\"? Tato akce nelze vzít zpět." 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/client/src/components/leo/records-case-number-column.tsx: -------------------------------------------------------------------------------- 1 | import type { Warrant } from "@snailycad/types"; 2 | import type { _Record } from "@snailycad/utils/case-number"; 3 | import { useFormatCaseNumber } from "hooks/use-format-case-number"; 4 | 5 | interface RecordsCaseNumberColumnProps { 6 | record: _Record | Warrant; 7 | } 8 | 9 | export function RecordsCaseNumberColumn(props: RecordsCaseNumberColumnProps) { 10 | const { formatCaseNumber } = useFormatCaseNumber(); 11 | return <>{formatCaseNumber(props.record)}; 12 | } 13 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220219080856_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DiscordRoles" ADD COLUMN "taxiRoleId" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "User" ADD COLUMN "isTaxi" BOOLEAN NOT NULL DEFAULT true; 6 | 7 | -- AlterTable 8 | ALTER TABLE "cad" ADD COLUMN "taxiWhitelisted" BOOLEAN NOT NULL DEFAULT false; 9 | 10 | -- AddForeignKey 11 | ALTER TABLE "DiscordRoles" ADD CONSTRAINT "DiscordRoles_taxiRoleId_fkey" FOREIGN KEY ("taxiRoleId") REFERENCES "DiscordRole"("id") ON DELETE SET NULL ON UPDATE CASCADE; 12 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220210162134_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "SeizedItem" ( 3 | "id" TEXT NOT NULL, 4 | "recordId" TEXT NOT NULL, 5 | "item" TEXT NOT NULL, 6 | "quantity" INTEGER NOT NULL DEFAULT 1, 7 | "illegal" BOOLEAN NOT NULL DEFAULT false, 8 | 9 | CONSTRAINT "SeizedItem_pkey" PRIMARY KEY ("id") 10 | ); 11 | 12 | -- AddForeignKey 13 | ALTER TABLE "SeizedItem" ADD CONSTRAINT "SeizedItem_recordId_fkey" FOREIGN KEY ("recordId") REFERENCES "Record"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 14 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230215183336_records_connections/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Record" ADD COLUMN "call911Id" TEXT, 3 | ADD COLUMN "incidentId" TEXT; 4 | 5 | -- AddForeignKey 6 | ALTER TABLE "Record" ADD CONSTRAINT "Record_call911Id_fkey" FOREIGN KEY ("call911Id") REFERENCES "Call911"("id") ON DELETE CASCADE ON UPDATE CASCADE; 7 | 8 | -- AddForeignKey 9 | ALTER TABLE "Record" ADD CONSTRAINT "Record_incidentId_fkey" FOREIGN KEY ("incidentId") REFERENCES "LeoIncident"("id") ON DELETE CASCADE ON UPDATE CASCADE; 10 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211101110733_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "cad" ADD COLUMN "apiTokenId" TEXT; 3 | 4 | -- CreateTable 5 | CREATE TABLE "ApiToken" ( 6 | "id" TEXT NOT NULL, 7 | "enabled" BOOLEAN NOT NULL DEFAULT false, 8 | "token" TEXT, 9 | "routes" TEXT[], 10 | 11 | CONSTRAINT "ApiToken_pkey" PRIMARY KEY ("id") 12 | ); 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "cad" ADD CONSTRAINT "cad_apiTokenId_fkey" FOREIGN KEY ("apiTokenId") REFERENCES "ApiToken"("id") ON DELETE SET NULL ON UPDATE CASCADE; 16 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211108153517_/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `_dlPilotCategoryToDLCategory` table. If the table is not empty, all the data it contains will be lost. 5 | 6 | */ 7 | -- DropForeignKey 8 | ALTER TABLE "_dlPilotCategoryToDLCategory" DROP CONSTRAINT "_dlPilotCategoryToDLCategory_A_fkey"; 9 | 10 | -- DropForeignKey 11 | ALTER TABLE "_dlPilotCategoryToDLCategory" DROP CONSTRAINT "_dlPilotCategoryToDLCategory_B_fkey"; 12 | 13 | -- DropTable 14 | DROP TABLE "_dlPilotCategoryToDLCategory"; 15 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221101091651_address_values/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "ValueType" ADD VALUE 'ADDRESS'; 3 | 4 | -- CreateTable 5 | CREATE TABLE "AddressValue" ( 6 | "id" TEXT NOT NULL, 7 | "valueId" TEXT NOT NULL, 8 | "county" TEXT, 9 | "postal" TEXT, 10 | 11 | CONSTRAINT "AddressValue_pkey" PRIMARY KEY ("id") 12 | ); 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "AddressValue" ADD CONSTRAINT "AddressValue_valueId_fkey" FOREIGN KEY ("valueId") REFERENCES "Value"("id") ON DELETE CASCADE ON UPDATE CASCADE; 16 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230908142856_department_links/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "DepartmentValueLink" ( 3 | "id" TEXT NOT NULL, 4 | "title" TEXT NOT NULL, 5 | "url" TEXT NOT NULL, 6 | "departmentId" TEXT NOT NULL, 7 | 8 | CONSTRAINT "DepartmentValueLink_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- AddForeignKey 12 | ALTER TABLE "DepartmentValueLink" ADD CONSTRAINT "DepartmentValueLink_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "DepartmentValue"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 13 | -------------------------------------------------------------------------------- /apps/api/src/exceptions/extended-bad-request.ts: -------------------------------------------------------------------------------- 1 | import type { ResponseErrorObject } from "@tsed/common"; 2 | import { BadRequest } from "@tsed/exceptions"; 3 | 4 | interface ErrorMessage { 5 | message: string; 6 | data: Record; 7 | } 8 | 9 | export class ExtendedBadRequest extends BadRequest implements ResponseErrorObject { 10 | headers = {}; 11 | errors = {}; 12 | 13 | constructor(errors: Record, message = "badRequest") { 14 | super(message); 15 | this.errors = errors; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /apps/client/locales/nl-BE/bleeter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Bleeter": { 3 | "bleeter": "Bleeter", 4 | "noPosts": "Er zijn nog geen posts.", 5 | "createBleet": "Maak Bleet ", 6 | "editBleet": "Pas Bleet aan", 7 | "deleteBleet": "Bleet verwijderen", 8 | "viewBleet": "Bleet bekijken", 9 | "headerImage": "Header foto", 10 | "bleetTitle": "Bleet titel", 11 | "bleetBody": "Bleet Body", 12 | "alert_deleteBleet": "Zeker dat je \"{title}\" wilt verwijderen? Deze actie kan niet ongedaan gemaakt worden." 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230114081441_audit_logs/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "AuditLog" ( 3 | "id" TEXT NOT NULL, 4 | "executorId" TEXT NOT NULL, 5 | "action" JSONB NOT NULL, 6 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 7 | "translationKey" TEXT, 8 | 9 | CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id") 10 | ); 11 | 12 | -- AddForeignKey 13 | ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_executorId_fkey" FOREIGN KEY ("executorId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 14 | -------------------------------------------------------------------------------- /apps/api/src/lib/data/prisma.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | import { setDefaultCadFeatures } from "migrations/set-default-cad-features"; 3 | 4 | export const prisma = new PrismaClient({ 5 | errorFormat: "colorless", 6 | log: ["info", "warn", "error"], 7 | }); 8 | 9 | async function handleMigrations() { 10 | await Promise.all([setDefaultCadFeatures()]); 11 | } 12 | 13 | try { 14 | void handleMigrations(); 15 | console.info("Successfully migrated."); 16 | } catch (e) { 17 | console.error("Could not migrate: ", e); 18 | } 19 | -------------------------------------------------------------------------------- /apps/client/src/hooks/shared/use-debounced-value.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useDebounce } from "react-use"; 3 | 4 | export function useDebouncedValue(_value: T, wait: number) { 5 | const [localValue, setLocalValue] = React.useState(_value); 6 | const [value, setValue] = React.useState(_value); 7 | 8 | useDebounce( 9 | () => { 10 | setValue(localValue); 11 | }, 12 | wait, 13 | [localValue], 14 | ); 15 | 16 | return { 17 | localValue, 18 | value, 19 | setLocalValue, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /apps/client/tests/classNames.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-self-compare */ 2 | /* eslint-disable @typescript-eslint/no-unnecessary-condition */ 3 | /* eslint-disable quotes */ 4 | import { expect, test } from "vitest"; 5 | import { classNames } from "../src/lib/classNames"; 6 | 7 | test("Should join classes together", () => { 8 | const t = 0 === 0; 9 | const f = 0 !== 0; 10 | 11 | expect( 12 | classNames("hello world", t && "true-thing", f && "not-shown", t && null, undefined), 13 | ).toMatchInlineSnapshot('"hello world true-thing"'); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/ui/src/components/stories/helpers/error-message.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { ErrorMessage } from "../../error-message"; 3 | 4 | const meta = { 5 | title: "Helpers/ErrorMessage", 6 | component: ErrorMessage, 7 | tags: ["autodocs"], 8 | } satisfies Meta; 9 | 10 | export default meta; 11 | type Story = StoryObj; 12 | 13 | export const Default: Story = { 14 | args: { 15 | errorMessage: "Password must be at least 8 characters long.", 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /apps/client/locales/en/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "Truck Logs", 4 | "createTruckLog": "Create Truck Log", 5 | "editTruckLog": "Edit Truck Log", 6 | "deleteTruckLog": "Delete Truck Log", 7 | "driver": "Driver", 8 | "vehicle": "Vehicle", 9 | "startedAt": "Started At", 10 | "endedAt": "Ended At", 11 | "noTruckLogs": "You don't have any truck logs yet.", 12 | "notes": "Notes", 13 | "alert_deleteTruckLog": "Are you sure you want to delete this truck log? This action cannot be undone." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230829143916_more_webhook_types/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | -- This migration adds more than one value to an enum. 3 | -- With PostgreSQL versions 11 and earlier, this is not possible 4 | -- in a single migration. This can be worked around by creating 5 | -- multiple migrations, each migration adding only one value to 6 | -- the enum. 7 | 8 | 9 | ALTER TYPE "DiscordWebhookType" ADD VALUE 'DEPARTMENT_WHITELIST_STATUS'; 10 | ALTER TYPE "DiscordWebhookType" ADD VALUE 'USER_WHITELIST_STATUS'; 11 | 12 | -- DropEnum 13 | DROP TYPE "StatusEnum"; 14 | -------------------------------------------------------------------------------- /apps/client/locales/cs-CZ/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "Truck Logs", 4 | "createTruckLog": "Create Truck Log", 5 | "editTruckLog": "Edit Truck Log", 6 | "deleteTruckLog": "Delete Truck Log", 7 | "driver": "Driver", 8 | "vehicle": "Vehicle", 9 | "startedAt": "Started At", 10 | "endedAt": "Ended At", 11 | "noTruckLogs": "You don't have any truck logs yet.", 12 | "notes": "Notes", 13 | "alert_deleteTruckLog": "Are you sure you want to delete this truck log? This action cannot be undone." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/client/locales/en-gb/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "Truck Logs", 4 | "createTruckLog": "Create Truck Log", 5 | "editTruckLog": "Edit Truck Log", 6 | "deleteTruckLog": "Delete Truck Log", 7 | "driver": "Driver", 8 | "vehicle": "Vehicle", 9 | "startedAt": "Started At", 10 | "endedAt": "Ended At", 11 | "noTruckLogs": "You don't have any truck logs yet.", 12 | "notes": "Notes", 13 | "alert_deleteTruckLog": "Are you sure you want to delete this truck log? This action cannot be undone." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221015061713_auto_911_call_events/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | -- This migration adds more than one value to an enum. 3 | -- With PostgreSQL versions 11 and earlier, this is not possible 4 | -- in a single migration. This can be worked around by creating 5 | -- multiple migrations, each migration adding only one value to 6 | -- the enum. 7 | 8 | 9 | ALTER TYPE "ShouldDoType" ADD VALUE 'EN_ROUTE'; 10 | ALTER TYPE "ShouldDoType" ADD VALUE 'ON_SCENE'; 11 | 12 | -- AlterTable 13 | ALTER TABLE "Call911Event" ADD COLUMN "translationData" JSONB; 14 | -------------------------------------------------------------------------------- /apps/api/src/utils/order-by.ts: -------------------------------------------------------------------------------- 1 | import set from "lodash.set"; 2 | 3 | export function getPrismaModelOrderBy(sorting: string) { 4 | return sorting.split(",").reduce((obj, cv) => { 5 | const [key, sortOrder] = cv.split(":") as [string, "asc" | "desc"]; 6 | return set(obj, key, sortOrder); 7 | }, {}); 8 | } 9 | 10 | export function getFirstOrderBy(sorting: string) { 11 | const [first] = sorting.split(","); 12 | if (!first) return null; 13 | 14 | const [key, sortOrder] = first.split(":") as [string, "asc" | "desc"]; 15 | return [key, sortOrder] as const; 16 | } 17 | -------------------------------------------------------------------------------- /apps/client/locales/pt-BR/bleeter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Bleeter": { 3 | "bleeter": "Bleeter", 4 | "noPosts": "Ainda não há mensagens no bleeter. Volte mais tarde", 5 | "createBleet": "Criar Bleet", 6 | "editBleet": "Editar Bleet", 7 | "deleteBleet": "Excluir Bleet", 8 | "viewBleet": "Ver Bleet", 9 | "headerImage": "Imagem de Cabeçalho", 10 | "bleetTitle": "Título do Bleet", 11 | "bleetBody": "Corpo do Bleet", 12 | "alert_deleteBleet": "Tem certeza que deseja excluir \"{title}\"? Esta ação não pode ser desfeita." 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/ui/src/components/stories/fields/date-picker-field.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { DatePickerField } from "../../fields/date-picker-field"; 3 | 4 | const meta = { 5 | title: "Inputs/DatePickerField", 6 | component: DatePickerField, 7 | tags: ["autodocs"], 8 | } satisfies Meta; 9 | 10 | export default meta; 11 | type Story = StoryObj; 12 | 13 | export const Default: Story = { 14 | args: { 15 | label: "Appointment Date", 16 | value: new Date("2023-03-15"), 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/utils/src/editor/index.ts: -------------------------------------------------------------------------------- 1 | import type { SlateElements, Text } from "./types"; 2 | import type { BaseEditor, Descendant } from "slate"; 3 | import type { ReactEditor } from "slate-react"; 4 | import type { HistoryEditor } from "slate-history"; 5 | 6 | export type SlateEditor = BaseEditor & ReactEditor & HistoryEditor; 7 | 8 | declare module "slate" { 9 | interface CustomTypes { 10 | Element: SlateElements; 11 | Text: Text; 12 | } 13 | } 14 | 15 | export * from "./types"; 16 | export { slateDataToString } from "./slate-data-to-string"; 17 | export type { Descendant }; 18 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220311162452_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "User" ADD COLUMN "soundSettingsId" TEXT; 3 | 4 | -- CreateTable 5 | CREATE TABLE "UserSoundSettings" ( 6 | "id" TEXT NOT NULL, 7 | "panicButton" BOOLEAN NOT NULL DEFAULT true, 8 | "signal100" BOOLEAN NOT NULL DEFAULT true, 9 | 10 | CONSTRAINT "UserSoundSettings_pkey" PRIMARY KEY ("id") 11 | ); 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "User" ADD CONSTRAINT "User_soundSettingsId_fkey" FOREIGN KEY ("soundSettingsId") REFERENCES "UserSoundSettings"("id") ON DELETE SET NULL ON UPDATE CASCADE; 15 | -------------------------------------------------------------------------------- /packages/ui/src/components/multi-form/multi-form-step.tsx: -------------------------------------------------------------------------------- 1 | import { type FormikProps, type FormikValues, useFormikContext } from "formik"; 2 | 3 | export interface MultiFormStepItem { 4 | title: string; 5 | id: string; 6 | isRequired?: boolean; 7 | children(formikState: FormikProps): React.JSX.Element | null; 8 | } 9 | 10 | export function MultiFormStep( 11 | props: MultiFormStepItem, 12 | ) { 13 | const formikState = useFormikContext(); 14 | return props.children(formikState); 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220529090004_/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "Notification" DROP CONSTRAINT "Notification_executorId_fkey"; 3 | 4 | -- DropForeignKey 5 | ALTER TABLE "User2FA" DROP CONSTRAINT "User2FA_userId_fkey"; 6 | 7 | -- AddForeignKey 8 | ALTER TABLE "User2FA" ADD CONSTRAINT "User2FA_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 9 | 10 | -- AddForeignKey 11 | ALTER TABLE "Notification" ADD CONSTRAINT "Notification_executorId_fkey" FOREIGN KEY ("executorId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 12 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220828120916_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "UserSession" ( 3 | "id" TEXT NOT NULL, 4 | "refreshToken" TEXT NOT NULL, 5 | "userId" TEXT NOT NULL, 6 | "expires" TIMESTAMP(3) NOT NULL, 7 | 8 | CONSTRAINT "UserSession_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- CreateIndex 12 | CREATE UNIQUE INDEX "UserSession_refreshToken_key" ON "UserSession"("refreshToken"); 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "UserSession" ADD CONSTRAINT "UserSession_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 16 | -------------------------------------------------------------------------------- /apps/client/locales/sv/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "Lastbilloggar", 4 | "createTruckLog": "Skapa lastbillogg", 5 | "editTruckLog": "Redigera lastbillogg", 6 | "deleteTruckLog": "Radera lastbillogg", 7 | "driver": "Förare", 8 | "vehicle": "Fordon", 9 | "startedAt": "Startade kl", 10 | "endedAt": "Avslutades kl", 11 | "noTruckLogs": "Du har inga lastbilloggar ännu.", 12 | "notes": "Anteckningar", 13 | "alert_deleteTruckLog": "Är du säker på att du vill radera denna lastbillogg? Denna åtgärd kan inte ångras." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/utils/src/utils/replace-template-variables.ts: -------------------------------------------------------------------------------- 1 | export function replaceTemplateVariables( 2 | template: string, 3 | replacers: Record, 4 | ) { 5 | const templateArr: (string | null)[] = template.split(/[{}]/); 6 | 7 | Object.entries(replacers).forEach(([replacer, value]) => { 8 | const idx = templateArr.indexOf(replacer); 9 | 10 | if (value) { 11 | templateArr[idx] = value.toString(); 12 | } else { 13 | templateArr[idx] = null; 14 | } 15 | }); 16 | 17 | return templateArr.filter((v) => v !== null).join(""); 18 | } 19 | -------------------------------------------------------------------------------- /apps/client/locales/fr-FR/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "Journaux de camions", 4 | "createTruckLog": "Créer un journal de camion", 5 | "editTruckLog": "Modifier le journal du camion", 6 | "deleteTruckLog": "Supprimer le journal du camion", 7 | "driver": "Conducteur", 8 | "vehicle": "Véhicule", 9 | "startedAt": "Commencé à", 10 | "endedAt": "Terminé à", 11 | "noTruckLogs": "Vous n'avez pas encore de logs de camion.", 12 | "notes": "Remarques", 13 | "alert_deleteTruckLog": "Voulez-vous vraiment supprimer ce journal de camion ? " 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/client/src/lib/table/get-contrasting-text-color.ts: -------------------------------------------------------------------------------- 1 | import Color from "color"; 2 | 3 | export function generateContrastColor(backgroundColor: string): string { 4 | try { 5 | const luminance = new Color(backgroundColor.trim()).luminosity(); 6 | return luminance > 0.2 ? "#000000" : "#FFFFFF"; 7 | } catch (err) { 8 | return "#FFFFFF"; 9 | } 10 | } 11 | 12 | export function darkenColor(color: string, amount: number): string { 13 | try { 14 | return new Color(color.trim()).darken(amount).hex(); 15 | } catch (err) { 16 | console.log(err); 17 | 18 | return color; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/ui/src/components/inputs/checkbox.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "mxcn"; 3 | 4 | type Props = React.JSX.IntrinsicElements["input"]; 5 | 6 | export const Checkbox = React.forwardRef((props, ref) => ( 7 | 17 | )); 18 | Checkbox.displayName = "Checkbox"; 19 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220223131255_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "IncidentEvent" ( 3 | "id" TEXT NOT NULL, 4 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 5 | "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 6 | "incidentId" TEXT NOT NULL, 7 | "description" TEXT NOT NULL, 8 | 9 | CONSTRAINT "IncidentEvent_pkey" PRIMARY KEY ("id") 10 | ); 11 | 12 | -- AddForeignKey 13 | ALTER TABLE "IncidentEvent" ADD CONSTRAINT "IncidentEvent_incidentId_fkey" FOREIGN KEY ("incidentId") REFERENCES "LeoIncident"("id") ON DELETE CASCADE ON UPDATE CASCADE; 14 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221015075722_citizen_license_numbers/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Citizen" ADD COLUMN "driversLicenseNumber" TEXT, 3 | ADD COLUMN "pilotLicenseNumber" TEXT, 4 | ADD COLUMN "waterLicenseNumber" TEXT, 5 | ADD COLUMN "weaponLicenseNumber" TEXT; 6 | 7 | -- AlterTable 8 | ALTER TABLE "MiscCadSettings" ADD COLUMN "driversLicenseNumberLength" INTEGER DEFAULT 8, 9 | ADD COLUMN "pilotLicenseNumberLength" INTEGER DEFAULT 6, 10 | ADD COLUMN "waterLicenseNumberLength" INTEGER DEFAULT 8, 11 | ADD COLUMN "weaponLicenseNumberLength" INTEGER DEFAULT 8; 12 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211222065948_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Call911" ADD COLUMN "positionId" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "MiscCadSettings" ADD COLUMN "liveMapURL" TEXT; 6 | 7 | -- CreateTable 8 | CREATE TABLE "Position" ( 9 | "id" TEXT NOT NULL, 10 | "lat" DOUBLE PRECISION, 11 | "lng" DOUBLE PRECISION, 12 | 13 | CONSTRAINT "Position_pkey" PRIMARY KEY ("id") 14 | ); 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "Call911" ADD CONSTRAINT "Call911_positionId_fkey" FOREIGN KEY ("positionId") REFERENCES "Position"("id") ON DELETE SET NULL ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /apps/client/locales/nl-BE/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "Vrachtwagen logs", 4 | "createTruckLog": "Creër vrachtwagen log", 5 | "editTruckLog": "Pas vrachtwagen log aan", 6 | "deleteTruckLog": "Verwijder vrachtwagen log", 7 | "driver": "Bestuurder", 8 | "vehicle": "Voertuig", 9 | "startedAt": "Begonnen op", 10 | "endedAt": "Gestopt op", 11 | "noTruckLogs": "U heeft nog geen vrachtwagen logs.", 12 | "notes": "Notities", 13 | "alert_deleteTruckLog": "Weet je zeker dat je deze vrachtwagen log wilt verwijderen ? Dit kan niet ongedaan worden." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230227154019_raw_webhooks/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "RawWebhook" ( 3 | "id" TEXT NOT NULL, 4 | "type" "DiscordWebhookType" NOT NULL, 5 | "url" TEXT NOT NULL, 6 | "miscCadSettingsId" TEXT, 7 | 8 | CONSTRAINT "RawWebhook_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- CreateIndex 12 | CREATE UNIQUE INDEX "RawWebhook_type_key" ON "RawWebhook"("type"); 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "RawWebhook" ADD CONSTRAINT "RawWebhook_miscCadSettingsId_fkey" FOREIGN KEY ("miscCadSettingsId") REFERENCES "MiscCadSettings"("id") ON DELETE CASCADE ON UPDATE CASCADE; 16 | -------------------------------------------------------------------------------- /apps/client/src/hooks/use-format-case-number.ts: -------------------------------------------------------------------------------- 1 | import { useAuth } from "context/AuthContext"; 2 | import { formatCaseNumber, type _Record } from "@snailycad/utils/case-number"; 3 | import type { Warrant } from "@snailycad/types"; 4 | 5 | export function useFormatCaseNumber() { 6 | const { cad } = useAuth(); 7 | const miscCadSettings = cad?.miscCadSettings; 8 | 9 | function _formatCaseNumber(record: _Record | Warrant | null) { 10 | if (!record) return ""; 11 | return formatCaseNumber(record, miscCadSettings?.caseNumberTemplate ?? null); 12 | } 13 | 14 | return { formatCaseNumber: _formatCaseNumber }; 15 | } 16 | -------------------------------------------------------------------------------- /apps/client/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { getSessionUser } from "lib/auth"; 2 | import type { GetServerSideProps } from "next"; 3 | 4 | export default function IndexPage() { 5 | return null; 6 | } 7 | 8 | export const getServerSideProps: GetServerSideProps = async ({ req }) => { 9 | const user = await getSessionUser(req); 10 | 11 | if (user) { 12 | return { 13 | redirect: { 14 | destination: "/citizen", 15 | permanent: false, 16 | }, 17 | }; 18 | } 19 | 20 | return { 21 | redirect: { 22 | destination: "/auth/login", 23 | permanent: false, 24 | }, 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/ui/src/components/loader.tsx: -------------------------------------------------------------------------------- 1 | import { useProgressBar } from "@react-aria/progress"; 2 | import { cn } from "mxcn"; 3 | 4 | interface Props { 5 | className?: string; 6 | } 7 | 8 | export function Loader(props: Props) { 9 | const { progressBarProps } = useProgressBar({ label: "loading..." }); 10 | 11 | return ( 12 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/cancel.yml: -------------------------------------------------------------------------------- 1 | name: Cancel 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - "main" 7 | pull_request: 8 | types: [opened, synchronize] 9 | 10 | jobs: 11 | cancel: 12 | name: "Cancel Previous Actions" 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 2 15 | steps: 16 | - uses: styfle/cancel-workflow-action@0.12.1 17 | with: 18 | # `60597620` = Chromatic 19 | # `14026219` = Lint 20 | # `19288873` = "Typecheck & test builds in workspaces/*" 21 | workflow_id: 60597620, 14026219, 19288873 22 | access_token: ${{ github.token }} 23 | -------------------------------------------------------------------------------- /apps/client/locales/de-DE/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "LKW-Protokolle", 4 | "createTruckLog": "LKW-Protokoll erstellen", 5 | "editTruckLog": "LKW-Protokoll bearbeiten", 6 | "deleteTruckLog": "LKW-Protokoll löschen", 7 | "driver": "Fahrer", 8 | "vehicle": "Fahrzeug", 9 | "startedAt": "Angefangen am", 10 | "endedAt": "Beendet am", 11 | "noTruckLogs": "Du hast noch keinen Lkw-Protokoll.", 12 | "notes": "Notizen", 13 | "alert_deleteTruckLog": "Bist du sicher, dass Du dieses LKW-Protokoll löschen willst? Diese Aktion kann nicht rückgängig gemacht werden!" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/client/locales/ru/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "Журналы грузоперевозок", 4 | "createTruckLog": "Создать запись о грузоперевозке", 5 | "editTruckLog": "Редактировать запись о грузоперевозке", 6 | "deleteTruckLog": "Удалить запись о грузоперевозке", 7 | "driver": "Водитель", 8 | "vehicle": "Транспорт", 9 | "startedAt": "Погрузка", 10 | "endedAt": "Выгрузка", 11 | "noTruckLogs": "У вас нет журналов грузоперевозок.", 12 | "notes": "Записи", 13 | "alert_deleteTruckLog": "Вы уверены, что хотите удалить запись о грузоперевозке? Это действие необратимо." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/client/locales/pt-BR/truck-logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "TruckLogs": { 3 | "truckLogs": "Registros do Caminhão", 4 | "createTruckLog": "Criar Log do Caminhão", 5 | "editTruckLog": "Editar Log do Caminhão", 6 | "deleteTruckLog": "Excluir log do Caminhão", 7 | "driver": "Condutor", 8 | "vehicle": "Veículo", 9 | "startedAt": "Iniciado em", 10 | "endedAt": "terminado em", 11 | "noTruckLogs": "Você ainda não tem nenhum log de caminhão.", 12 | "notes": "Notas", 13 | "alert_deleteTruckLog": "Tem certeza de que deseja excluir este log de caminhão? Esta ação não pode ser desfeita." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/ui/src/components/error-message.tsx: -------------------------------------------------------------------------------- 1 | import type { DOMAttributes } from "@react-types/shared"; 2 | import { ExclamationTriangleFill } from "react-bootstrap-icons"; 3 | 4 | interface Props { 5 | errorMessageProps?: DOMAttributes; 6 | errorMessage: React.ReactNode; 7 | } 8 | 9 | export function ErrorMessage(props: Props) { 10 | return ( 11 | 15 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220122070726_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "cad" ADD COLUMN "autoSetUserPropertiesId" TEXT; 3 | 4 | -- CreateTable 5 | CREATE TABLE "AutoSetPropertiesUser" ( 6 | "id" TEXT NOT NULL, 7 | "leo" BOOLEAN DEFAULT false, 8 | "dispatch" BOOLEAN DEFAULT false, 9 | "emsFd" BOOLEAN DEFAULT false, 10 | 11 | CONSTRAINT "AutoSetPropertiesUser_pkey" PRIMARY KEY ("id") 12 | ); 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "cad" ADD CONSTRAINT "cad_autoSetUserPropertiesId_fkey" FOREIGN KEY ("autoSetUserPropertiesId") REFERENCES "AutoSetPropertiesUser"("id") ON DELETE SET NULL ON UPDATE CASCADE; 16 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220228155753_leo_roles_discord/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "_leoRoles" ( 3 | "A" TEXT NOT NULL, 4 | "B" TEXT NOT NULL 5 | ); 6 | 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "_leoRoles_AB_unique" ON "_leoRoles"("A", "B"); 9 | 10 | -- CreateIndex 11 | CREATE INDEX "_leoRoles_B_index" ON "_leoRoles"("B"); 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "_leoRoles" ADD FOREIGN KEY ("A") REFERENCES "DiscordRole"("id") ON DELETE CASCADE ON UPDATE CASCADE; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_leoRoles" ADD FOREIGN KEY ("B") REFERENCES "DiscordRoles"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220317151747_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "_emsFdRoles" ( 3 | "A" TEXT NOT NULL, 4 | "B" TEXT NOT NULL 5 | ); 6 | 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "_emsFdRoles_AB_unique" ON "_emsFdRoles"("A", "B"); 9 | 10 | -- CreateIndex 11 | CREATE INDEX "_emsFdRoles_B_index" ON "_emsFdRoles"("B"); 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "_emsFdRoles" ADD FOREIGN KEY ("A") REFERENCES "DiscordRole"("id") ON DELETE CASCADE ON UPDATE CASCADE; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_emsFdRoles" ADD FOREIGN KEY ("B") REFERENCES "DiscordRoles"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /apps/api/src/middlewares/active-deputy.ts: -------------------------------------------------------------------------------- 1 | import { Context, Middleware, Req, type MiddlewareMethods, Res, Next } from "@tsed/common"; 2 | import { getSessionUser } from "lib/auth/getSessionUser"; 3 | import { getActiveDeputy } from "lib/get-active-ems-fd-deputy"; 4 | 5 | @Middleware() 6 | export class ActiveDeputy implements MiddlewareMethods { 7 | async use(@Req() req: Req, @Res() res: Res, @Context() ctx: Context, @Next() next: Next) { 8 | const user = await getSessionUser({ req, res }); 9 | const deputy = await getActiveDeputy({ req, user, ctx }); 10 | 11 | ctx.set("activeDeputy", deputy); 12 | 13 | next(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220115075142_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'ACTIVE_DISPATCHERS'; 3 | 4 | -- CreateTable 5 | CREATE TABLE "ActiveDispatchers" ( 6 | "id" TEXT NOT NULL, 7 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 8 | "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 | "userId" TEXT NOT NULL, 10 | 11 | CONSTRAINT "ActiveDispatchers_pkey" PRIMARY KEY ("id") 12 | ); 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "ActiveDispatchers" ADD CONSTRAINT "ActiveDispatchers_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; 16 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220904060949_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Call911" ADD COLUMN "gtaMapPositionId" TEXT; 3 | 4 | -- CreateTable 5 | CREATE TABLE "GTAMapPosition" ( 6 | "id" TEXT NOT NULL, 7 | "x" DOUBLE PRECISION NOT NULL, 8 | "y" DOUBLE PRECISION NOT NULL, 9 | "z" DOUBLE PRECISION NOT NULL, 10 | "heading" INTEGER NOT NULL, 11 | 12 | CONSTRAINT "GTAMapPosition_pkey" PRIMARY KEY ("id") 13 | ); 14 | 15 | -- AddForeignKey 16 | ALTER TABLE "Call911" ADD CONSTRAINT "Call911_gtaMapPositionId_fkey" FOREIGN KEY ("gtaMapPositionId") REFERENCES "GTAMapPosition"("id") ON DELETE SET NULL ON UPDATE CASCADE; 17 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220601115342_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "CombinedLeoUnit" ADD COLUMN "activeIncidentId" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "EmsFdDeputy" ADD COLUMN "activeIncidentId" TEXT; 6 | 7 | -- AddForeignKey 8 | ALTER TABLE "CombinedLeoUnit" ADD CONSTRAINT "CombinedLeoUnit_activeIncidentId_fkey" FOREIGN KEY ("activeIncidentId") REFERENCES "LeoIncident"("id") ON DELETE SET NULL ON UPDATE CASCADE; 9 | 10 | -- AddForeignKey 11 | ALTER TABLE "EmsFdDeputy" ADD CONSTRAINT "EmsFdDeputy_activeIncidentId_fkey" FOREIGN KEY ("activeIncidentId") REFERENCES "LeoIncident"("id") ON DELETE SET NULL ON UPDATE CASCADE; 12 | -------------------------------------------------------------------------------- /apps/client/src/hooks/useImageUrl.ts: -------------------------------------------------------------------------------- 1 | import { getAPIUrl } from "@snailycad/utils/api-url"; 2 | import { IMAGES_REGEX } from "@snailycad/config"; 3 | 4 | type ImageURLTypes = "citizens" | "users" | "bleeter" | "units" | "cad" | "values" | "pets"; 5 | 6 | export function useImageUrl() { 7 | function makeImageUrl(type: ImageURLTypes, id: string | null) { 8 | if (!id) return; 9 | 10 | if (id.match(IMAGES_REGEX)) { 11 | return id; 12 | } 13 | 14 | const url = getAPIUrl().replace("/v1", ""); 15 | const IMAGE_URL = `${url}/static/`; 16 | 17 | return `${IMAGE_URL}${type}/${id}`; 18 | } 19 | 20 | return { makeImageUrl }; 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/src/components/stories/fields/checkbox.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { CheckboxField } from "../../fields/checkbox-field"; 3 | 4 | const meta = { 5 | title: "Inputs/CheckboxField", 6 | component: CheckboxField, 7 | tags: ["autodocs"], 8 | } satisfies Meta; 9 | 10 | export default meta; 11 | type Story = StoryObj; 12 | 13 | export const Default: Story = { 14 | args: { 15 | children: "Unsubscribe", 16 | }, 17 | }; 18 | 19 | export const Indeterminate: Story = { 20 | args: { 21 | children: "Unsubscribe", 22 | isIndeterminate: true, 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220321151728_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'CUSTOM_TEXTFIELD_VALUES'; 3 | 4 | -- CreateTable 5 | CREATE TABLE "CadFeature" ( 6 | "id" TEXT NOT NULL, 7 | "feature" "Feature" NOT NULL, 8 | "isEnabled" BOOLEAN NOT NULL DEFAULT false, 9 | "cadId" TEXT, 10 | 11 | CONSTRAINT "CadFeature_pkey" PRIMARY KEY ("id") 12 | ); 13 | 14 | -- CreateIndex 15 | CREATE UNIQUE INDEX "CadFeature_feature_key" ON "CadFeature"("feature"); 16 | 17 | -- AddForeignKey 18 | ALTER TABLE "CadFeature" ADD CONSTRAINT "CadFeature_cadId_fkey" FOREIGN KEY ("cadId") REFERENCES "cad"("id") ON DELETE SET NULL ON UPDATE CASCADE; 19 | -------------------------------------------------------------------------------- /packages/README.md: -------------------------------------------------------------------------------- 1 | # `packages` directory 2 | 3 | This directory holds all the packages for SnailyCADv4 to function. 4 | 5 | - `config/`: This package holds some simple configuration that is shared between the API and client. 6 | - `permissions/`: This package holds all SnailyCADv4's permissions. It also exports functions to check user permissions. 7 | - `schemas/`: This package exports validation schemas. This is used by both the client and API to validate form fields. 8 | - `types/`: This package exports types that are generated by Prisma. These types come from the database. 9 | - `utils/`: This package exports several util functions that are primarily used by the client. 10 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211228091734_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "PenalCode" ADD COLUMN "groupId" TEXT, 3 | ADD COLUMN "position" INTEGER; 4 | 5 | -- CreateTable 6 | CREATE TABLE "PenalCodeGroup" ( 7 | "id" TEXT NOT NULL, 8 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 | "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 10 | "name" TEXT NOT NULL, 11 | 12 | CONSTRAINT "PenalCodeGroup_pkey" PRIMARY KEY ("id") 13 | ); 14 | 15 | -- AddForeignKey 16 | ALTER TABLE "PenalCode" ADD CONSTRAINT "PenalCode_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "PenalCodeGroup"("id") ON DELETE SET NULL ON UPDATE CASCADE; 17 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221216054554_image_blur_data/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "BleeterPost" ADD COLUMN "imageBlurData" TEXT; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Citizen" ADD COLUMN "imageBlurData" TEXT; 6 | 7 | -- AlterTable 8 | ALTER TABLE "EmsFdDeputy" ADD COLUMN "imageBlurData" TEXT; 9 | 10 | -- AlterTable 11 | ALTER TABLE "Officer" ADD COLUMN "imageBlurData" TEXT; 12 | 13 | -- AlterTable 14 | ALTER TABLE "QualificationValue" ADD COLUMN "imageBlurData" TEXT; 15 | 16 | -- AlterTable 17 | ALTER TABLE "Value" ADD COLUMN "officerRankImageBlurData" TEXT; 18 | 19 | -- AlterTable 20 | ALTER TABLE "cad" ADD COLUMN "logoBlurData" TEXT; 21 | -------------------------------------------------------------------------------- /apps/api/src/lib/images/validate-image-url.ts: -------------------------------------------------------------------------------- 1 | import { IMAGES_REGEX } from "@snailycad/config"; 2 | import { ExtendedBadRequest } from "~/exceptions/extended-bad-request"; 3 | 4 | export function validateImageURL(image: unknown) { 5 | if (image === null) return null; 6 | if (!image) return undefined; 7 | 8 | if (typeof image === "string" && image.includes("fakepath")) { 9 | return undefined; 10 | } 11 | 12 | if (typeof image !== "string") { 13 | throw new ExtendedBadRequest({ image: "invalidImageURL" }); 14 | } 15 | 16 | if (!image.match(IMAGES_REGEX)) { 17 | throw new ExtendedBadRequest({ image: "invalidImageURL" }); 18 | } 19 | 20 | return image; 21 | } 22 | -------------------------------------------------------------------------------- /apps/api/src/instrument.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from "@sentry/node"; 2 | 3 | Sentry.init({ 4 | dsn: "https://308dd96b826c4e38a814fc9bae681687@o518232.ingest.sentry.io/6553288", 5 | integrations: [ 6 | Sentry.httpIntegration(), 7 | Sentry.prismaIntegration(), 8 | Sentry.nestIntegration(), 9 | Sentry.consoleIntegration(), 10 | Sentry.onUnhandledRejectionIntegration(), 11 | Sentry.onUncaughtExceptionIntegration(), 12 | ], 13 | tracesSampleRate: 1.0, 14 | profilesSampleRate: 1.0, 15 | attachStacktrace: true, 16 | ignoreErrors: [/can't reach database server at/gim], 17 | denyUrls: [/localhost/], 18 | enabled: process.env.NODE_ENV !== "development", 19 | }); 20 | -------------------------------------------------------------------------------- /apps/client/src/lib/values/normalize-value.ts: -------------------------------------------------------------------------------- 1 | import type { ValueType } from "@snailycad/types"; 2 | 3 | // transform: PENAL_CODES -> penalCodes 4 | // transform: DEPARTMENT -> department 5 | export function normalizeValue(value: ValueType | (string & {})) { 6 | let split = value.toLowerCase().split(/_/); 7 | 8 | if (split.length > 1) { 9 | split = split.map((valueType, idx) => { 10 | if (idx > 0) { 11 | const firstLetter = valueType.charAt(0); 12 | 13 | return [firstLetter.toUpperCase(), valueType.substring(1).toLowerCase()].join(""); 14 | } 15 | 16 | return valueType.toLowerCase(); 17 | }); 18 | } 19 | 20 | return split.join(""); 21 | } 22 | -------------------------------------------------------------------------------- /packages/schemas/src/dispatch/index.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export * from "./911-calls"; 4 | export * from "./bolos"; 5 | 6 | export const UPDATE_AOP_SCHEMA = z.object({ 7 | aop: z.string().min(1).max(255), 8 | }); 9 | 10 | export const UPDATE_RADIO_CHANNEL_SCHEMA = z.object({ 11 | radioChannel: z.string().nullable(), 12 | }); 13 | 14 | export const TONES_SCHEMA = z.object({ 15 | leoTone: z.boolean(), 16 | emsFdTone: z.boolean(), 17 | description: z.string().max(355).nullish(), 18 | }); 19 | 20 | export const PRIVATE_MESSAGE_SCHEMA = z.object({ 21 | message: z.string().min(1), 22 | call911Id: z.string().nullish(), 23 | incidentId: z.string().nullish(), 24 | }); 25 | -------------------------------------------------------------------------------- /apps/client/src/components/citizen/manage-citizen-form/create-officer-step.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { ValueType } from "@snailycad/types"; 3 | import { ManageOfficerFields } from "components/leo/manage-officer/manage-officer-fields"; 4 | import { useLoadValuesClientSide } from "hooks/useLoadValuesClientSide"; 5 | 6 | let valuesFetched = false; 7 | 8 | export function CreateOfficerStep() { 9 | useLoadValuesClientSide({ 10 | valueTypes: [ValueType.DEPARTMENT, ValueType.DIVISION], 11 | enabled: !valuesFetched, 12 | }); 13 | 14 | React.useEffect(() => { 15 | valuesFetched = true; 16 | }, []); 17 | 18 | return ; 19 | } 20 | -------------------------------------------------------------------------------- /apps/client/src/state/search/weapon-search-state.ts: -------------------------------------------------------------------------------- 1 | import type { PostLeoSearchWeaponData } from "@snailycad/types/api"; 2 | import { shallow } from "zustand/shallow"; 3 | import { createWithEqualityFn } from "zustand/traditional"; 4 | 5 | export type WeaponSearchResult = PostLeoSearchWeaponData; 6 | 7 | interface WeaponSearchState { 8 | currentResult: WeaponSearchResult | null | undefined; 9 | setCurrentResult(v: WeaponSearchResult | null | undefined): void; 10 | } 11 | 12 | export const useWeaponSearch = createWithEqualityFn()( 13 | (set) => ({ 14 | currentResult: undefined, 15 | setCurrentResult: (v) => set({ currentResult: v }), 16 | }), 17 | shallow, 18 | ); 19 | -------------------------------------------------------------------------------- /apps/client/locales/tc/ems-fd.json: -------------------------------------------------------------------------------- 1 | { 2 | "Ems": { 3 | "emsFd": "EMS/FD", 4 | "createMedicalRecord": "創建醫療記錄", 5 | "searchMedicalRecord": "搜索醫療記錄", 6 | "activeDeputies": "積極代表", 7 | "selectDeputy": "選擇副手", 8 | "deputy": "副手", 9 | "myDeputies": "我的副手", 10 | "createDeputy": "創建副手", 11 | "editDeputy": "編輯副手", 12 | "deputyName": "副名", 13 | "deleteDeputy": "刪除副手", 14 | "noDeputies": "你沒有任何副手", 15 | "noActiveDeputies": "沒有現役代表", 16 | "citizenNoMedicalRecords": "這個公民沒有任何醫療記錄。", 17 | "activeDeputy": "現役副手", 18 | "declareDead": "宣布死亡", 19 | "declareAlive": "宣布活著", 20 | "alert_deleteDeputy": "是否確實要刪除 {deputy}?此操作無法撤消。" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220505140647_/migration.sql: -------------------------------------------------------------------------------- 1 | -- DropForeignKey 2 | ALTER TABLE "EmsFdDeputy" DROP CONSTRAINT "EmsFdDeputy_departmentId_fkey"; 3 | 4 | -- AlterTable 5 | ALTER TABLE "EmsFdDeputy" ADD COLUMN "whitelistStatusId" TEXT, 6 | ALTER COLUMN "departmentId" DROP NOT NULL; 7 | 8 | -- AddForeignKey 9 | ALTER TABLE "EmsFdDeputy" ADD CONSTRAINT "EmsFdDeputy_departmentId_fkey" FOREIGN KEY ("departmentId") REFERENCES "DepartmentValue"("id") ON DELETE SET NULL ON UPDATE CASCADE; 10 | 11 | -- AddForeignKey 12 | ALTER TABLE "EmsFdDeputy" ADD CONSTRAINT "EmsFdDeputy_whitelistStatusId_fkey" FOREIGN KEY ("whitelistStatusId") REFERENCES "LeoWhitelistStatus"("id") ON DELETE SET NULL ON UPDATE CASCADE; 13 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220602141240_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'COURTHOUSE_POSTS'; 3 | 4 | -- CreateTable 5 | CREATE TABLE "CourthousePost" ( 6 | "id" TEXT NOT NULL, 7 | "userId" TEXT NOT NULL, 8 | "title" VARCHAR(255) NOT NULL, 9 | "descriptionData" JSONB, 10 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 11 | "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 12 | 13 | CONSTRAINT "CourthousePost_pkey" PRIMARY KEY ("id") 14 | ); 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "CourthousePost" ADD CONSTRAINT "CourthousePost_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /apps/client/locales/cn/ems-fd.json: -------------------------------------------------------------------------------- 1 | { 2 | "Ems": { 3 | "emsFd": "EMS/FD", 4 | "createMedicalRecord": "创建医疗记录", 5 | "searchMedicalRecord": "搜索医疗记录", 6 | "activeDeputies": "可用人员", 7 | "selectDeputy": "选择人员", 8 | "deputy": "人员", 9 | "myDeputies": "我的人员", 10 | "createDeputy": "创建人员", 11 | "editDeputy": "编辑人员", 12 | "deputyName": "人员名称", 13 | "deleteDeputy": "删除人员", 14 | "noDeputies": "你没有任何人员", 15 | "noActiveDeputies": "没有可用人员", 16 | "citizenNoMedicalRecords": "这个公民没有任何医疗记录。", 17 | "activeDeputy": "活跃的人员", 18 | "declareDead": "宣布死亡", 19 | "declareAlive": "宣布活着", 20 | "alert_deleteDeputy": "是否确实要删除 {deputy}?此操作无法撤消。" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/client/src/hooks/global/useAreaOfPlay.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { useAuth } from "context/AuthContext"; 3 | import { useListener } from "@casperiv/use-socket.io"; 4 | import { SocketEvents } from "@snailycad/config"; 5 | 6 | export function useAreaOfPlay() { 7 | const { cad } = useAuth(); 8 | const [aop, setAop] = React.useState(cad?.areaOfPlay ?? ""); 9 | 10 | const showAop = cad?.features?.AOP ?? true; 11 | 12 | useListener(SocketEvents.UpdateAreaOfPlay, (aop: string | null) => { 13 | setAop(aop ?? ""); 14 | }); 15 | 16 | React.useEffect(() => { 17 | setAop(cad?.areaOfPlay ?? ""); 18 | }, [cad?.areaOfPlay]); 19 | 20 | return { showAop, areaOfPlay: aop }; 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/src/components/form-row.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "mxcn"; 3 | 4 | type Props = React.PropsWithoutRef & { 5 | children: React.ReactNode; 6 | /** use flexbox instead of grid (Allows for children to be variable length) */ 7 | useFlex?: boolean; 8 | }; 9 | 10 | export function FormRow({ useFlex = false, children, className, ...rest }: Props) { 11 | return ( 12 |
20 | {children} 21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220310160602_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "ValueType" ADD VALUE 'CITIZEN_FLAG'; 3 | 4 | -- CreateTable 5 | CREATE TABLE "_citizenFlags" ( 6 | "A" TEXT NOT NULL, 7 | "B" TEXT NOT NULL 8 | ); 9 | 10 | -- CreateIndex 11 | CREATE UNIQUE INDEX "_citizenFlags_AB_unique" ON "_citizenFlags"("A", "B"); 12 | 13 | -- CreateIndex 14 | CREATE INDEX "_citizenFlags_B_index" ON "_citizenFlags"("B"); 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_citizenFlags" ADD FOREIGN KEY ("A") REFERENCES "Citizen"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "_citizenFlags" ADD FOREIGN KEY ("B") REFERENCES "Value"("id") ON DELETE CASCADE ON UPDATE CASCADE; 21 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230222185840_admin_roles_arr/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "_adminRoles" ( 3 | "A" TEXT NOT NULL, 4 | "B" TEXT NOT NULL 5 | ); 6 | 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "_adminRoles_AB_unique" ON "_adminRoles"("A", "B"); 9 | 10 | -- CreateIndex 11 | CREATE INDEX "_adminRoles_B_index" ON "_adminRoles"("B"); 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "_adminRoles" ADD CONSTRAINT "_adminRoles_A_fkey" FOREIGN KEY ("A") REFERENCES "DiscordRole"("id") ON DELETE CASCADE ON UPDATE CASCADE; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_adminRoles" ADD CONSTRAINT "_adminRoles_B_fkey" FOREIGN KEY ("B") REFERENCES "DiscordRoles"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /apps/api/src/utils/businesses.ts: -------------------------------------------------------------------------------- 1 | import { type cad, WhitelistStatus } from "@prisma/client"; 2 | import { BadRequest } from "@tsed/exceptions"; 3 | import { prisma } from "lib/data/prisma"; 4 | 5 | export async function validateBusinessAcceptance(cad: cad, businessId: string) { 6 | if (cad.businessWhitelisted) { 7 | const business = await prisma.business.findUnique({ 8 | where: { 9 | id: businessId, 10 | }, 11 | }); 12 | 13 | if (!business || business.status === WhitelistStatus.DECLINED) { 14 | throw new BadRequest("notFound"); 15 | } 16 | 17 | if (business.status === WhitelistStatus.PENDING) { 18 | throw new BadRequest("businessIsPending"); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/client/locales/cn/auth.json: -------------------------------------------------------------------------------- 1 | { 2 | "Auth": { 3 | "register": "注册", 4 | "login": "登录", 5 | "createAccount": "创建帐户", 6 | "username": "用户名", 7 | "password": "密码", 8 | "confirmPassword": "确认密码", 9 | "noAccount": "没有帐户?在此注册", 10 | "hasAccount": "已有帐户?在此登录", 11 | "show": "显示", 12 | "hide": "隐藏", 13 | "registrationCode": "注册码", 14 | "changePassword": "更改密码", 15 | "totpCode": "2FA代码", 16 | "twoFactorCode": "双因素代码", 17 | "savePasswordInfo": "确保安全地保存您的新密码,以免这种情况再次发生!,确保安全地保存您的新密码,以免这种情况再次发生!", 18 | "loginViaDiscord": "通过不和谐登录", 19 | "continueAs": "继续为 {username}", 20 | "sixDigitCode": "请输入验证器应用中的 6 位数代码", 21 | "loginViaSteam": "通过蒸汽登录" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/client/locales/tc/auth.json: -------------------------------------------------------------------------------- 1 | { 2 | "Auth": { 3 | "register": "註冊", 4 | "login": "登錄", 5 | "createAccount": "創建帳戶", 6 | "username": "用戶名", 7 | "password": "密碼", 8 | "confirmPassword": "確認密碼", 9 | "noAccount": "沒有帳戶?在此註冊", 10 | "hasAccount": "已有帳戶?在此登錄", 11 | "show": "顯示", 12 | "hide": "隱藏", 13 | "registrationCode": "註冊碼", 14 | "changePassword": "更改密碼", 15 | "totpCode": "2FA代碼", 16 | "twoFactorCode": "雙因素代碼", 17 | "savePasswordInfo": "確保安全地保存您的新密碼,以免這種情況再次發生!,確保安全地保存您的新密碼,以免這種情況再次發生!", 18 | "loginViaDiscord": "通過不和諧登錄", 19 | "continueAs": "繼續為 {username}", 20 | "sixDigitCode": "請輸入驗證器應用中的 6 位數代碼", 21 | "loginViaSteam": "通過Steam登錄" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/client/src/state/search/vehicle-search-state.ts: -------------------------------------------------------------------------------- 1 | import type { PostLeoSearchVehicleData } from "@snailycad/types/api"; 2 | import { createWithEqualityFn } from "zustand/traditional"; 3 | import { shallow } from "zustand/shallow"; 4 | 5 | export type VehicleSearchResult = NonNullable; 6 | 7 | interface VehicleSearchState { 8 | currentResult: VehicleSearchResult | null | "initial"; 9 | setCurrentResult(v: VehicleSearchResult | null | "initial"): void; 10 | } 11 | 12 | export const useVehicleSearch = createWithEqualityFn()( 13 | (set) => ({ 14 | currentResult: "initial", 15 | setCurrentResult: (v) => set({ currentResult: v }), 16 | }), 17 | shallow, 18 | ); 19 | -------------------------------------------------------------------------------- /packages/ui/src/components/stories/editors/json-editor.stories.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import type { Meta, StoryObj } from "@storybook/react"; 3 | import { JsonEditor } from "../../editors/json-editor"; 4 | 5 | const meta = { 6 | title: "Editors/JSON Editor", 7 | component: JsonEditor, 8 | tags: ["autodocs"], 9 | } satisfies Meta; 10 | 11 | export default meta; 12 | type Story = StoryObj; 13 | 14 | function DefaultRenderer() { 15 | const [value, setValue] = React.useState(""); 16 | 17 | return ; 18 | } 19 | 20 | export const Default: Story = { 21 | render: () => , 22 | }; 23 | -------------------------------------------------------------------------------- /apps/api/src/lib/citizen/validate-ssn.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "lib/data/prisma"; 2 | import { ExtendedBadRequest } from "~/exceptions/extended-bad-request"; 3 | 4 | interface ValidateSocialSecurityNumber { 5 | socialSecurityNumber: string; 6 | citizenId?: string; 7 | } 8 | 9 | export async function validateSocialSecurityNumber(options: ValidateSocialSecurityNumber) { 10 | const existing = await prisma.citizen.findFirst({ 11 | where: { 12 | socialSecurityNumber: options.socialSecurityNumber, 13 | NOT: { 14 | id: options.citizenId, 15 | }, 16 | }, 17 | }); 18 | 19 | if (existing) { 20 | throw new ExtendedBadRequest({ socialSecurityNumber: "socialSecurityNumberTaken" }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /scripts/validate.mjs: -------------------------------------------------------------------------------- 1 | import { bold, underline, yellow } from "colorette"; 2 | 3 | const [major, minor] = process.version.split(".").map((v) => { 4 | return parseInt(v.replace("v", "")); 5 | }); 6 | 7 | const is16 = major >= 16; 8 | const isDot6 = major === 16 ? minor >= 6 : !(major < 16); 9 | const isSupportedVersion = is16 && isDot6; 10 | const versionText = bold(underline("v16.6 or higher")); 11 | 12 | if (!isSupportedVersion) { 13 | throw warn( 14 | `Unsupported Node.js version detected (${process.version}). SnailyCADv4 requires version ${versionText}. See https://docs.snailycad.org/docs/errors/invalid-node-version`, 15 | ); 16 | } 17 | 18 | function warn(message) { 19 | console.warn(`${yellow("warn")} -`, message); 20 | } 21 | -------------------------------------------------------------------------------- /packages/ui/src/components/breadcrumbs/breadcrumbs.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { type AriaBreadcrumbsProps, useBreadcrumbs } from "@react-aria/breadcrumbs"; 3 | 4 | interface Props extends AriaBreadcrumbsProps { 5 | children: React.ReactNode; 6 | } 7 | 8 | export function Breadcrumbs(props: Props) { 9 | const { navProps } = useBreadcrumbs(props); 10 | const children = React.Children.toArray(props.children); 11 | 12 | return ( 13 | 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220118171251_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "_registeredBusinessVehicles" ( 3 | "A" TEXT NOT NULL, 4 | "B" TEXT NOT NULL 5 | ); 6 | 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "_registeredBusinessVehicles_AB_unique" ON "_registeredBusinessVehicles"("A", "B"); 9 | 10 | -- CreateIndex 11 | CREATE INDEX "_registeredBusinessVehicles_B_index" ON "_registeredBusinessVehicles"("B"); 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "_registeredBusinessVehicles" ADD FOREIGN KEY ("A") REFERENCES "Business"("id") ON DELETE CASCADE ON UPDATE CASCADE; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_registeredBusinessVehicles" ADD FOREIGN KEY ("B") REFERENCES "RegisteredVehicle"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221004064740_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Citizen" ADD COLUMN "suspendedLicensesId" TEXT; 3 | 4 | -- CreateTable 5 | CREATE TABLE "SuspendedCitizenLicenses" ( 6 | "id" TEXT NOT NULL, 7 | "driverLicense" BOOLEAN NOT NULL DEFAULT false, 8 | "pilotLicense" BOOLEAN NOT NULL DEFAULT false, 9 | "waterLicense" BOOLEAN NOT NULL DEFAULT false, 10 | "firearmsLicense" BOOLEAN NOT NULL DEFAULT false, 11 | 12 | CONSTRAINT "SuspendedCitizenLicenses_pkey" PRIMARY KEY ("id") 13 | ); 14 | 15 | -- AddForeignKey 16 | ALTER TABLE "Citizen" ADD CONSTRAINT "Citizen_suspendedLicensesId_fkey" FOREIGN KEY ("suspendedLicensesId") REFERENCES "SuspendedCitizenLicenses"("id") ON DELETE SET NULL ON UPDATE CASCADE; 17 | -------------------------------------------------------------------------------- /packages/ui/src/components/infofield.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "mxcn"; 3 | 4 | type ParagraphProps = React.PropsWithoutRef; 5 | interface Props extends ParagraphProps { 6 | label: string | React.ReactNode; 7 | childrenProps?: React.PropsWithoutRef; 8 | } 9 | 10 | export function Infofield({ childrenProps, label, ...props }: Props) { 11 | return ( 12 |
13 | {label}: 14 | 15 | {props.children} 16 | 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20221224115442_persistent_tones/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "ActiveToneType" AS ENUM ('LEO', 'EMS_FD', 'SHARED'); 3 | 4 | -- CreateTable 5 | CREATE TABLE "ActiveTone" ( 6 | "id" TEXT NOT NULL, 7 | "type" "ActiveToneType" NOT NULL, 8 | "description" TEXT, 9 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 10 | "createdById" TEXT NOT NULL, 11 | 12 | CONSTRAINT "ActiveTone_pkey" PRIMARY KEY ("id") 13 | ); 14 | 15 | -- CreateIndex 16 | CREATE UNIQUE INDEX "ActiveTone_type_key" ON "ActiveTone"("type"); 17 | 18 | -- AddForeignKey 19 | ALTER TABLE "ActiveTone" ADD CONSTRAINT "ActiveTone_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 20 | -------------------------------------------------------------------------------- /apps/api/src/lib/auth/setUserPreferencesCookies.ts: -------------------------------------------------------------------------------- 1 | import type { Res } from "@tsed/common"; 2 | import { setCookie } from "utils/set-cookie"; 3 | 4 | interface Options { 5 | res: Res; 6 | locale: string | null; 7 | isDarkTheme: boolean; 8 | } 9 | 10 | export function setUserPreferencesCookies(options: Options) { 11 | const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1000; 12 | setCookie({ 13 | name: "sn_locale", 14 | res: options.res, 15 | value: options.locale ?? "", 16 | expires: options.locale ? ONE_YEAR_MS : 0, 17 | httpOnly: false, 18 | }); 19 | setCookie({ 20 | name: "sn_isDarkTheme", 21 | res: options.res, 22 | value: String(options.isDarkTheme), 23 | expires: ONE_YEAR_MS, 24 | httpOnly: false, 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | dist 4 | .env 5 | 6 | # uploaded images 7 | apps/api/public/**/*.png 8 | apps/api/public/**/*.svg 9 | apps/api/public/**/*.jpg 10 | apps/api/public/**/*.jpeg 11 | apps/api/public/**/*.gif 12 | apps/api/public/**/*.webp 13 | 14 | # custom sounds & custom favicon 15 | apps/client/public/favicon.png 16 | apps/client/public/sounds/*.mp3 17 | 18 | # eslint 19 | .eslintcache 20 | 21 | # typescript 22 | tsconfig.tsbuildinfo 23 | 24 | # misc 25 | .husky/_ 26 | .turbo 27 | apps/**/coverage 28 | packages/**/coverage 29 | import-officers.mjs 30 | sample-data.json 31 | .data 32 | .dev-data 33 | # Sentry 34 | .sentryclirc 35 | 36 | # storybook 37 | storybook-static 38 | build-storybook.log 39 | 40 | # Prisma generated types 41 | **/prisma/index.ts -------------------------------------------------------------------------------- /apps/client/src/pages/api-docs.tsx: -------------------------------------------------------------------------------- 1 | import { Loader } from "@snailycad/ui"; 2 | import { getAPIUrl } from "@snailycad/utils/api-url"; 3 | import type { GetServerSideProps } from "next"; 4 | 5 | export default function ApiDocs() { 6 | return ( 7 |
8 | 9 | 10 | 11 |
12 | ); 13 | } 14 | 15 | export const getServerSideProps: GetServerSideProps = async () => { 16 | const apiURL = getAPIUrl(); 17 | const destination = apiURL.replace("/v1", "/api-docs"); 18 | 19 | return { 20 | redirect: { 21 | destination, 22 | permanent: true, 23 | }, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /packages/ui/src/components/stories/fields/switch-field.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { SwitchField } from "../../fields/switch-field"; 3 | 4 | const meta = { 5 | title: "Inputs/SwitchField", 6 | component: SwitchField, 7 | tags: ["autodocs"], 8 | } satisfies Meta; 9 | 10 | export default meta; 11 | type Story = StoryObj; 12 | 13 | export const Default: Story = { 14 | args: { 15 | children: "Dark Mode", 16 | }, 17 | }; 18 | 19 | export const Disabled: Story = { 20 | args: { 21 | children: "Dark Mode", 22 | isDisabled: true, 23 | }, 24 | }; 25 | 26 | export const HiddenLabel: Story = { 27 | args: { 28 | "aria-label": "Add helpful label here", 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /apps/api/src/utils/jwt.ts: -------------------------------------------------------------------------------- 1 | import process from "node:process"; 2 | import { sign, verify } from "jsonwebtoken"; 3 | 4 | export function signJWT(value: any, expiresInSeconds: number | string) { 5 | const secret = process.env.JWT_SECRET; 6 | 7 | if (!secret) { 8 | throw new Error("No JWT_SECRET env var was found"); 9 | } 10 | 11 | return sign(value, secret, { expiresIn: expiresInSeconds }); 12 | } 13 | 14 | export function verifyJWT(value: string) { 15 | const secret = process.env.JWT_SECRET; 16 | 17 | if (!secret) { 18 | throw new Error("No JWT_SECRET env var was found"); 19 | } 20 | 21 | try { 22 | return verify(value, secret) as { userId: string; sessionId: string; exp: number; iat: number }; 23 | } catch { 24 | return null; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220526080330_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "ValueType" ADD VALUE 'CALL_TYPE'; 3 | 4 | -- AlterTable 5 | ALTER TABLE "Call911" ADD COLUMN "typeId" TEXT; 6 | 7 | -- CreateTable 8 | CREATE TABLE "CallTypeValue" ( 9 | "id" TEXT NOT NULL, 10 | "priority" INTEGER, 11 | "valueId" TEXT NOT NULL, 12 | 13 | CONSTRAINT "CallTypeValue_pkey" PRIMARY KEY ("id") 14 | ); 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "CallTypeValue" ADD CONSTRAINT "CallTypeValue_valueId_fkey" FOREIGN KEY ("valueId") REFERENCES "Value"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "Call911" ADD CONSTRAINT "Call911_typeId_fkey" FOREIGN KEY ("typeId") REFERENCES "CallTypeValue"("id") ON DELETE SET NULL ON UPDATE CASCADE; 21 | -------------------------------------------------------------------------------- /apps/client/locales/zh-CN/bleeter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Bleeter": { 3 | "bleeter": "Bleeter", 4 | "noPosts": "目前还没有 Bleeter 帖子。请稍后再查看。", 5 | "createBleet": "创建 Bleet", 6 | "editBleet": "编辑 Bleet", 7 | "deleteBleet": "删除 Bleet", 8 | "viewBleet": "查看 Bleet", 9 | "headerImage": "标题图像", 10 | "bleetTitle": "Bleet 标题", 11 | "bleetBody": "Bleet 正文", 12 | "followers": "关注者", 13 | "following": "正在关注", 14 | "follow": "关注", 15 | "unfollow": "取消关注", 16 | "editProfile": "编辑个人资料", 17 | "posts": "帖子", 18 | "myProfile": "我的个人资料", 19 | "save": "保存", 20 | "getStarted": "开始使用", 21 | "unVerifyProfile": "取消验证个人资料", 22 | "verifyProfile": "验证个人资料", 23 | "alert_deleteBleet": "您确定要删除 \"{title}\" 吗?此操作无法撤消。" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /apps/client/src/hooks/leo/use-get-user-officers.ts: -------------------------------------------------------------------------------- 1 | import type { GetMyOfficersData } from "@snailycad/types/api"; 2 | import { useQuery } from "@tanstack/react-query"; 3 | import useFetch from "lib/useFetch"; 4 | 5 | export function useUserOfficers(options?: { enabled?: boolean }) { 6 | const { execute } = useFetch(); 7 | 8 | const { data, isLoading } = useQuery({ 9 | refetchOnWindowFocus: false, 10 | ...(options ?? {}), 11 | queryKey: ["/leo"], 12 | queryFn: async () => { 13 | const { json } = await execute({ path: "/leo" }); 14 | 15 | if (Array.isArray(json.officers)) { 16 | return json.officers; 17 | } 18 | 19 | return []; 20 | }, 21 | }); 22 | 23 | return { userOfficers: data ?? [], isLoading }; 24 | } 25 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230718154053_weapon_flags/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "ValueType" ADD VALUE 'WEAPON_FLAG'; 3 | 4 | -- CreateTable 5 | CREATE TABLE "_weaponFlags" ( 6 | "A" TEXT NOT NULL, 7 | "B" TEXT NOT NULL 8 | ); 9 | 10 | -- CreateIndex 11 | CREATE UNIQUE INDEX "_weaponFlags_AB_unique" ON "_weaponFlags"("A", "B"); 12 | 13 | -- CreateIndex 14 | CREATE INDEX "_weaponFlags_B_index" ON "_weaponFlags"("B"); 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_weaponFlags" ADD CONSTRAINT "_weaponFlags_A_fkey" FOREIGN KEY ("A") REFERENCES "Value"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "_weaponFlags" ADD CONSTRAINT "_weaponFlags_B_fkey" FOREIGN KEY ("B") REFERENCES "Weapon"("id") ON DELETE CASCADE ON UPDATE CASCADE; 21 | -------------------------------------------------------------------------------- /apps/api/src/lib/discord/utils.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "lib/data/prisma"; 2 | 3 | export function encode(obj: Record) { 4 | let string = ""; 5 | 6 | for (const [key, value] of Object.entries(obj)) { 7 | if (!value) continue; 8 | string += `&${encodeURIComponent(key)}=${encodeURIComponent(`${value}`)}`; 9 | } 10 | 11 | return string.substring(1); 12 | } 13 | 14 | export async function isDiscordIdInUse(discordId: string, userId: string) { 15 | const existing = await prisma.user.findFirst({ 16 | where: { 17 | discordId, 18 | }, 19 | }); 20 | 21 | return existing && userId !== existing.id; 22 | } 23 | 24 | export function parseDiscordGuildIds(guildId: string) { 25 | const guildIds = guildId.split(","); 26 | return guildIds; 27 | } 28 | -------------------------------------------------------------------------------- /apps/api/src/lib/images/get-image-webp-path.ts: -------------------------------------------------------------------------------- 1 | import type { Buffer } from "node:buffer"; 2 | import process from "node:process"; 3 | import sharp from "sharp"; 4 | import { randomUUID } from "node:crypto"; 5 | 6 | export interface RawImageToWebPOptions { 7 | buffer: Buffer; 8 | id?: string; 9 | pathType: "cad" | "citizens" | "users" | "bleeter" | "units" | "values" | "pets"; 10 | } 11 | 12 | export async function getImageWebPPath(options: RawImageToWebPOptions) { 13 | const sharpImage = sharp(options.buffer).webp(); 14 | const buffer = await sharpImage.toBuffer(); 15 | 16 | const id = options.id ?? randomUUID(); 17 | const fileName = `${id}.webp`; 18 | const path = `${process.cwd()}/public/${options.pathType}/${fileName}`; 19 | 20 | return { buffer, fileName, path }; 21 | } 22 | -------------------------------------------------------------------------------- /packages/config/src/index.ts: -------------------------------------------------------------------------------- 1 | export enum Cookie { 2 | AccessToken = "snaily-cad-session", 3 | RefreshToken = "snaily-cad-refresh-token", 4 | } 5 | 6 | export const API_TOKEN_HEADER = "snaily-cad-api-token" as const; 7 | 8 | /** the header which is used by a user to connect to the API with their token (Not JWT) */ 9 | export const USER_API_TOKEN_HEADER = "snaily-cad-user-api-token" as const; 10 | 11 | export type AllowedFileExtension = (typeof allowedFileExtensions)[number]; 12 | export const allowedFileExtensions = [ 13 | "image/png", 14 | "image/gif", 15 | "image/jpeg", 16 | "image/jpg", 17 | "image/webp", 18 | ] as const; 19 | export const IMAGES_REGEX = /https:\/\/(i.imgur.com|cdn.discordapp.com)\/.+/gi; 20 | 21 | export * from "./socket-events"; 22 | export * from "./routes"; 23 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220102070719_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Call911" ADD COLUMN "ended" BOOLEAN DEFAULT false; 3 | 4 | -- CreateTable 5 | CREATE TABLE "_Call911ToLeoIncident" ( 6 | "A" TEXT NOT NULL, 7 | "B" TEXT NOT NULL 8 | ); 9 | 10 | -- CreateIndex 11 | CREATE UNIQUE INDEX "_Call911ToLeoIncident_AB_unique" ON "_Call911ToLeoIncident"("A", "B"); 12 | 13 | -- CreateIndex 14 | CREATE INDEX "_Call911ToLeoIncident_B_index" ON "_Call911ToLeoIncident"("B"); 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_Call911ToLeoIncident" ADD FOREIGN KEY ("A") REFERENCES "Call911"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "_Call911ToLeoIncident" ADD FOREIGN KEY ("B") REFERENCES "LeoIncident"("id") ON DELETE CASCADE ON UPDATE CASCADE; 21 | -------------------------------------------------------------------------------- /apps/client/src/hooks/ems-fd/use-get-user-deputies.ts: -------------------------------------------------------------------------------- 1 | import type { GetMyDeputiesData } from "@snailycad/types/api"; 2 | import { useQuery } from "@tanstack/react-query"; 3 | import useFetch from "lib/useFetch"; 4 | 5 | export function useGetUserDeputies(options?: { enabled?: boolean }) { 6 | const { execute } = useFetch(); 7 | 8 | const { data, isLoading } = useQuery({ 9 | refetchOnWindowFocus: false, 10 | ...(options ?? {}), 11 | queryKey: ["/ems-fd"], 12 | queryFn: async () => { 13 | const { json } = await execute({ path: "/ems-fd" }); 14 | 15 | if (Array.isArray(json.deputies)) { 16 | return json.deputies; 17 | } 18 | 19 | return []; 20 | }, 21 | }); 22 | 23 | return { userDeputies: data ?? [], isLoading }; 24 | } 25 | -------------------------------------------------------------------------------- /apps/client/src/hooks/use-invalidate-query.ts: -------------------------------------------------------------------------------- 1 | import { useQueryClient } from "@tanstack/react-query"; 2 | 3 | /** 4 | * this hook is used to invalidate a query by passing parts of the `queryKey`. This is useful when you 5 | * want to invalidate a query where you don't have access to the full `queryKey` 6 | */ 7 | export function useInvalidateQuery(queryKeyParts: T) { 8 | const queryClient = useQueryClient(); 9 | 10 | const queries = queryClient.getQueryCache().findAll(); 11 | const query = queries.find((q) => queryKeyParts.every((k) => q.queryKey.includes(k))); 12 | 13 | async function invalidateQuery() { 14 | await queryClient.invalidateQueries({ queryKey: query?.queryKey }); 15 | return queryKeyParts; 16 | } 17 | 18 | return { invalidateQuery }; 19 | } 20 | -------------------------------------------------------------------------------- /apps/api/src/utils/file.ts: -------------------------------------------------------------------------------- 1 | import type { PlatformMulterFile } from "@tsed/common"; 2 | import { BadRequest } from "@tsed/exceptions"; 3 | import { ExtendedBadRequest } from "~/exceptions/extended-bad-request"; 4 | 5 | export function parseImportFile(file: PlatformMulterFile | undefined) { 6 | if (!file) { 7 | throw new ExtendedBadRequest({ file: "No file provided." }); 8 | } 9 | 10 | if (file.mimetype !== "application/json") { 11 | throw new BadRequest("invalidImageType"); 12 | } 13 | 14 | const rawBody = file.buffer.toString("utf8").trim(); 15 | let body = null; 16 | 17 | try { 18 | body = JSON.parse(rawBody); 19 | } catch { 20 | body = null; 21 | } 22 | 23 | if (!body) { 24 | throw new BadRequest("couldNotParseBody"); 25 | } 26 | 27 | return body; 28 | } 29 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220519162327_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "_officerRankDepartments" ( 3 | "A" TEXT NOT NULL, 4 | "B" TEXT NOT NULL 5 | ); 6 | 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "_officerRankDepartments_AB_unique" ON "_officerRankDepartments"("A", "B"); 9 | 10 | -- CreateIndex 11 | CREATE INDEX "_officerRankDepartments_B_index" ON "_officerRankDepartments"("B"); 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "_officerRankDepartments" ADD CONSTRAINT "_officerRankDepartments_A_fkey" FOREIGN KEY ("A") REFERENCES "DepartmentValue"("id") ON DELETE CASCADE ON UPDATE CASCADE; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_officerRankDepartments" ADD CONSTRAINT "_officerRankDepartments_B_fkey" FOREIGN KEY ("B") REFERENCES "Value"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /apps/api/src/lib/discord/config.ts: -------------------------------------------------------------------------------- 1 | import process from "node:process"; 2 | import { REST } from "@discordjs/rest"; 3 | 4 | export const DISCORD_API_VERSION = "10" as const; 5 | export const DISCORD_API_URL = `https://discord.com/api/v${DISCORD_API_VERSION}`; 6 | export const GUILD_ID = process.env.DISCORD_SERVER_ID; 7 | export const BOT_TOKEN = process.env.DISCORD_BOT_TOKEN; 8 | 9 | let cacheREST: REST | undefined; 10 | export function getRest(): REST { 11 | if (!BOT_TOKEN || BOT_TOKEN === "undefined") { 12 | throw new Error("mustSetBotTokenGuildId"); 13 | } 14 | 15 | cacheREST ??= new REST({ version: DISCORD_API_VERSION }).setToken(BOT_TOKEN); 16 | 17 | if (process.env.NODE_ENV === "development") { 18 | cacheREST.on("restDebug", console.info); 19 | } 20 | 21 | return cacheREST; 22 | } 23 | -------------------------------------------------------------------------------- /apps/api/src/utils/generate-string.ts: -------------------------------------------------------------------------------- 1 | import { customAlphabet } from "nanoid"; 2 | 3 | interface Options { 4 | extraChars?: string; 5 | type: "letters-only" | "numbers-only" | "all"; 6 | } 7 | 8 | export const NUMBERS = "0123456789"; 9 | export const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 10 | 11 | export function generateString(length: number, options?: Options) { 12 | const { type = "all", extraChars = "" } = options ?? {}; 13 | const alphabet = []; 14 | 15 | if (type === "numbers-only") { 16 | alphabet.push(...NUMBERS); 17 | } else if (type === "letters-only") { 18 | alphabet.push(...LETTERS); 19 | } else { 20 | alphabet.push(...NUMBERS, ...LETTERS); 21 | } 22 | 23 | const generate = customAlphabet([...alphabet, extraChars].join("")); 24 | return generate(length); 25 | } 26 | -------------------------------------------------------------------------------- /apps/client/src/state/ems-fd-state.ts: -------------------------------------------------------------------------------- 1 | import type { CombinedEmsFdUnit, EmsFdDeputy } from "@snailycad/types"; 2 | import { shallow } from "zustand/shallow"; 3 | import { createWithEqualityFn } from "zustand/traditional"; 4 | 5 | export type ActiveDeputy = EmsFdDeputy | CombinedEmsFdUnit; 6 | 7 | interface EmsFdState { 8 | activeDeputy: ActiveDeputy | null; 9 | setActiveDeputy(deputy: ActiveDeputy | null): void; 10 | 11 | deputies: EmsFdDeputy[]; 12 | setDeputies(deputies: EmsFdDeputy[]): void; 13 | } 14 | 15 | export const useEmsFdState = createWithEqualityFn()( 16 | (set) => ({ 17 | activeDeputy: null, 18 | setActiveDeputy: (deputy) => set({ activeDeputy: deputy }), 19 | 20 | deputies: [], 21 | setDeputies: (deputies) => set({ deputies }), 22 | }), 23 | shallow, 24 | ); 25 | -------------------------------------------------------------------------------- /packages/utils/src/editor/slate-data-to-string.ts: -------------------------------------------------------------------------------- 1 | import { Editor, Element as SlateElement, type Descendant } from "slate"; 2 | 3 | export function slateDataToString(data: Descendant[] | null) { 4 | const string: string[] = []; 5 | if (!data) return null; 6 | 7 | for (const item of data) { 8 | if (Editor.isEditor(item)) continue; 9 | 10 | if (SlateElement.isElement(item) && item.type === "bulleted-list") { 11 | const children = item.children?.flatMap((c) => c.children).map((v) => v?.text) ?? []; 12 | 13 | string.push(children.join(" ")); 14 | continue; 15 | } 16 | 17 | if (SlateElement.isElement(item)) { 18 | item.children?.forEach((child) => { 19 | string.push(child.text.trim()); 20 | }); 21 | } 22 | } 23 | 24 | return string.join(" "); 25 | } 26 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220430073214_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "DiscordWebhookType" AS ENUM ('CALL_911', 'PANIC_BUTTON', 'UNIT_STATUS', 'BOLO'); 3 | 4 | -- CreateTable 5 | CREATE TABLE "DiscordWebhook" ( 6 | "id" TEXT NOT NULL, 7 | "type" "DiscordWebhookType" NOT NULL, 8 | "webhookId" TEXT, 9 | "channelId" TEXT NOT NULL, 10 | "extraMessage" TEXT, 11 | "miscCadSettingsId" TEXT, 12 | 13 | CONSTRAINT "DiscordWebhook_pkey" PRIMARY KEY ("id") 14 | ); 15 | 16 | -- CreateIndex 17 | CREATE UNIQUE INDEX "DiscordWebhook_type_key" ON "DiscordWebhook"("type"); 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "DiscordWebhook" ADD CONSTRAINT "DiscordWebhook_miscCadSettingsId_fkey" FOREIGN KEY ("miscCadSettingsId") REFERENCES "MiscCadSettings"("id") ON DELETE CASCADE ON UPDATE CASCADE; 21 | -------------------------------------------------------------------------------- /packages/schemas/src/admin/import/vehicles.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { INSPECTION_STATUS_REGEX, TAX_STATUS_REGEX } from "../../citizen"; 3 | 4 | export const VEHICLE_SCHEMA = z.object({ 5 | plate: z.string().min(2).max(255), 6 | userId: z.string().nullish(), 7 | modelId: z.string().min(2).max(255), 8 | ownerId: z.string().min(2).max(255), 9 | registrationStatusId: z.string().min(2).max(255), 10 | insuranceStatus: z.string().max(255).nullish(), 11 | color: z.string().min(2).max(255), 12 | reportedStolen: z.boolean().nullish(), 13 | taxStatus: z.string().regex(TAX_STATUS_REGEX).nullish(), 14 | inspectionStatus: z.string().regex(INSPECTION_STATUS_REGEX).nullish(), 15 | flags: z.array(z.string()).nullish(), 16 | }); 17 | 18 | export const VEHICLE_SCHEMA_ARR = z.array(VEHICLE_SCHEMA).min(1); 19 | -------------------------------------------------------------------------------- /packages/schemas/src/tow.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | 3 | export const TOW_SCHEMA = z.object({ 4 | location: z.string().min(2).max(255), 5 | description: z.string().nullish(), 6 | descriptionData: z.any().nullish(), 7 | creatorId: z.string().max(255).nullish(), 8 | name: z.string().max(255).nullish(), 9 | postal: z.string().max(255).nullish(), 10 | plate: z.string().max(255).optional(), 11 | call911Id: z.string().max(255).optional(), 12 | deliveryAddressId: z.string().max(255).optional(), 13 | callCountyService: z.boolean().optional(), 14 | }); 15 | 16 | export const UPDATE_TOW_SCHEMA = TOW_SCHEMA.pick({ 17 | location: true, 18 | description: true, 19 | descriptionData: true, 20 | postal: true, 21 | name: true, 22 | }).extend({ 23 | assignedUnitId: z.string().max(255).nullable(), 24 | }); 25 | -------------------------------------------------------------------------------- /apps/api/src/lib/leo/records/create-citizen-violations.ts: -------------------------------------------------------------------------------- 1 | import type { Feature } from "@prisma/client"; 2 | import { upsertRecord } from "./upsert-record"; 3 | import type { z } from "zod"; 4 | import type { CREATE_TICKET_SCHEMA } from "@snailycad/schemas"; 5 | 6 | interface Options { 7 | cad: { features?: Record }; 8 | data: z.infer[]; 9 | citizenId: string; 10 | } 11 | 12 | export async function createCitizenViolations(options: Options) { 13 | try { 14 | await Promise.all( 15 | options.data.map((violation) => 16 | upsertRecord({ 17 | data: { ...violation, citizenId: options.citizenId }, 18 | recordId: null, 19 | cad: options.cad, 20 | }), 21 | ), 22 | ); 23 | } catch { 24 | /* empty */ 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20211231080948_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "RecordLog" ( 3 | "id" TEXT NOT NULL, 4 | "citizenId" TEXT NOT NULL, 5 | "recordId" TEXT, 6 | "warrantId" TEXT, 7 | 8 | CONSTRAINT "RecordLog_pkey" PRIMARY KEY ("id") 9 | ); 10 | 11 | -- AddForeignKey 12 | ALTER TABLE "RecordLog" ADD CONSTRAINT "RecordLog_citizenId_fkey" FOREIGN KEY ("citizenId") REFERENCES "Citizen"("id") ON DELETE CASCADE ON UPDATE CASCADE; 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "RecordLog" ADD CONSTRAINT "RecordLog_recordId_fkey" FOREIGN KEY ("recordId") REFERENCES "Record"("id") ON DELETE SET NULL ON UPDATE CASCADE; 16 | 17 | -- AddForeignKey 18 | ALTER TABLE "RecordLog" ADD CONSTRAINT "RecordLog_warrantId_fkey" FOREIGN KEY ("warrantId") REFERENCES "Warrant"("id") ON DELETE SET NULL ON UPDATE CASCADE; 19 | -------------------------------------------------------------------------------- /packages/ui/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from "@storybook/react-vite"; 2 | import { mergeConfig } from "vite"; 3 | const config: StorybookConfig = { 4 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 5 | addons: [ 6 | "@storybook/addon-links", 7 | "@storybook/addon-essentials", 8 | "@storybook/addon-interactions", 9 | "@storybook/addon-themes", 10 | "@storybook/addon-a11y", 11 | ], 12 | framework: { 13 | name: "@storybook/react-vite", 14 | options: {}, 15 | }, 16 | docs: { 17 | autodocs: "tag", 18 | }, 19 | async viteFinal(config) { 20 | return mergeConfig(config, { 21 | define: { "process.env": {} }, 22 | rollupOptions: { 23 | external: "@snailycad/utils", 24 | }, 25 | }); 26 | }, 27 | }; 28 | export default config; 29 | -------------------------------------------------------------------------------- /packages/ui/src/components/stories/helpers/label.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | import { Label } from "../../label"; 3 | 4 | const meta = { 5 | title: "Helpers/Label", 6 | component: Label, 7 | tags: ["autodocs"], 8 | } satisfies Meta; 9 | 10 | export default meta; 11 | type Story = StoryObj; 12 | 13 | export const Default: Story = { 14 | args: { 15 | label: "Password", 16 | labelProps: {}, 17 | }, 18 | }; 19 | 20 | export const Optional: Story = { 21 | args: { 22 | label: "Email", 23 | labelProps: {}, 24 | isOptional: true, 25 | }, 26 | }; 27 | 28 | export const Description: Story = { 29 | args: { 30 | label: "Email", 31 | labelProps: {}, 32 | description: "We will never share your email with anyone else.", 33 | }, 34 | }; 35 | -------------------------------------------------------------------------------- /apps/client/src/hooks/usePermission.ts: -------------------------------------------------------------------------------- 1 | import { Permissions, hasPermission, getPermissions } from "@snailycad/permissions"; 2 | import type { User } from "@snailycad/types"; 3 | import { useAuth } from "context/AuthContext"; 4 | 5 | export { Permissions }; 6 | export function usePermission() { 7 | const { user } = useAuth(); 8 | 9 | function _hasPermission(permissionsToCheck: Permissions[], userToCheck: User | null = user) { 10 | if (!userToCheck) return false; 11 | 12 | return hasPermission({ 13 | permissionsToCheck, 14 | userToCheck, 15 | }); 16 | } 17 | 18 | function _getPermissions(userToCheck: User | null = user) { 19 | if (!userToCheck) return false; 20 | 21 | return getPermissions(userToCheck); 22 | } 23 | 24 | return { hasPermissions: _hasPermission, getPermissions: _getPermissions }; 25 | } 26 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220516165653_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "DiscordRoles" ADD COLUMN "courthouseRolePermissions" TEXT[]; 3 | 4 | -- CreateTable 5 | CREATE TABLE "_courthouseRoles" ( 6 | "A" TEXT NOT NULL, 7 | "B" TEXT NOT NULL 8 | ); 9 | 10 | -- CreateIndex 11 | CREATE UNIQUE INDEX "_courthouseRoles_AB_unique" ON "_courthouseRoles"("A", "B"); 12 | 13 | -- CreateIndex 14 | CREATE INDEX "_courthouseRoles_B_index" ON "_courthouseRoles"("B"); 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_courthouseRoles" ADD CONSTRAINT "_courthouseRoles_A_fkey" FOREIGN KEY ("A") REFERENCES "DiscordRole"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "_courthouseRoles" ADD CONSTRAINT "_courthouseRoles_B_fkey" FOREIGN KEY ("B") REFERENCES "DiscordRoles"("id") ON DELETE CASCADE ON UPDATE CASCADE; 21 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220903062552_/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateEnum 2 | CREATE TYPE "ToAddDefaultPermissionsKey" AS ENUM ('MANAGE_WARRANTS_PERMISSIONS'); 3 | 4 | -- CreateTable 5 | CREATE TABLE "ToAddDefaultPermissions" ( 6 | "id" TEXT NOT NULL, 7 | "key" "ToAddDefaultPermissionsKey" NOT NULL, 8 | "userId" TEXT NOT NULL, 9 | "permissions" TEXT[], 10 | "addedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 11 | 12 | CONSTRAINT "ToAddDefaultPermissions_pkey" PRIMARY KEY ("id") 13 | ); 14 | 15 | -- CreateIndex 16 | CREATE UNIQUE INDEX "ToAddDefaultPermissions_key_userId_key" ON "ToAddDefaultPermissions"("key", "userId"); 17 | 18 | -- AddForeignKey 19 | ALTER TABLE "ToAddDefaultPermissions" ADD CONSTRAINT "ToAddDefaultPermissions_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; 20 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20231122144146_use_updated_at_units/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "CombinedEmsFdUnit" DROP COLUMN "lastStatusChangeTimestamp", 3 | ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 4 | ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; 5 | 6 | -- AlterTable 7 | ALTER TABLE "CombinedLeoUnit" DROP COLUMN "lastStatusChangeTimestamp", 8 | ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 9 | ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; 10 | 11 | -- AlterTable 12 | ALTER TABLE "EmsFdDeputy" DROP COLUMN "lastStatusChangeTimestamp"; 13 | 14 | -- AlterTable 15 | ALTER TABLE "MiscCadSettings" DROP COLUMN "inactivityTimeout"; 16 | 17 | -- AlterTable 18 | ALTER TABLE "Officer" DROP COLUMN "lastStatusChangeTimestamp"; 19 | -------------------------------------------------------------------------------- /apps/client/src/hooks/shared/useTemporaryItem.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export type GetIdFromObjFunc = (obj: Obj) => Id; 4 | export function useTemporaryItem( 5 | data: Obj[], 6 | getIdFromObj?: GetIdFromObjFunc, 7 | ) { 8 | const [tempId, setTempId] = React.useState(null); 9 | 10 | const tempItem = React.useMemo(() => { 11 | if (!tempId) return null; 12 | 13 | const item = data.find((obj) => { 14 | const id = getIdFromObj?.(obj) ?? obj["id"]; 15 | return id === tempId; 16 | }); 17 | 18 | return item ?? null; 19 | // eslint-disable-next-line react-hooks/exhaustive-deps 20 | }, [tempId, data]); 21 | 22 | const state = { 23 | tempId, 24 | setTempId, 25 | }; 26 | 27 | return [tempItem, state] as const; 28 | } 29 | -------------------------------------------------------------------------------- /packages/ui/src/components/stories/status/full-date.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/react"; 2 | 3 | import { FullDate } from "../../full-date"; 4 | 5 | const meta = { 6 | title: "Status/FullDate", 7 | component: FullDate, 8 | tags: ["autodocs"], 9 | } satisfies Meta; 10 | 11 | export default meta; 12 | type Story = StoryObj; 13 | 14 | export const Default: Story = { 15 | args: { 16 | children: new Date("2022-03-20 12:00:00").getTime(), 17 | }, 18 | }; 19 | 20 | export const Relative: Story = { 21 | args: { 22 | children: new Date("2022-03-20 12:00:00").getTime(), 23 | relative: true, 24 | }, 25 | }; 26 | 27 | export const OnlyShowTheDate: Story = { 28 | args: { 29 | children: new Date("2000-03-20 12:00:00").getTime(), 30 | onlyDate: true, 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20220221182451_/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "Feature" ADD VALUE 'ACTIVE_INCIDENTS'; 3 | 4 | -- DropForeignKey 5 | ALTER TABLE "LeoIncident" DROP CONSTRAINT "LeoIncident_creatorId_fkey"; 6 | 7 | -- AlterTable 8 | ALTER TABLE "LeoIncident" ADD COLUMN "isActive" BOOLEAN NOT NULL DEFAULT false, 9 | ALTER COLUMN "creatorId" DROP NOT NULL; 10 | 11 | -- AlterTable 12 | ALTER TABLE "Officer" ADD COLUMN "activeIncidentId" TEXT; 13 | 14 | -- AddForeignKey 15 | ALTER TABLE "Officer" ADD CONSTRAINT "Officer_activeIncidentId_fkey" FOREIGN KEY ("activeIncidentId") REFERENCES "LeoIncident"("id") ON DELETE SET NULL ON UPDATE CASCADE; 16 | 17 | -- AddForeignKey 18 | ALTER TABLE "LeoIncident" ADD CONSTRAINT "LeoIncident_creatorId_fkey" FOREIGN KEY ("creatorId") REFERENCES "Officer"("id") ON DELETE SET NULL ON UPDATE CASCADE; 19 | -------------------------------------------------------------------------------- /apps/api/src/middlewares/active-officer.ts: -------------------------------------------------------------------------------- 1 | import { Context, Middleware, Req, type MiddlewareMethods, Res, Next } from "@tsed/common"; 2 | import { Unauthorized } from "@tsed/exceptions"; 3 | import { getSessionUser } from "lib/auth/getSessionUser"; 4 | import { getActiveOfficer } from "lib/leo/activeOfficer"; 5 | 6 | @Middleware() 7 | export class ActiveOfficer implements MiddlewareMethods { 8 | async use(@Req() req: Req, @Res() res: Res, @Context() ctx: Context, @Next() next: Next) { 9 | const user = await getSessionUser({ req, res }).catch(() => null); 10 | if (!user && !req.originalUrl.includes("/v1/records")) { 11 | throw new Unauthorized("Unauthorized"); 12 | } 13 | 14 | const officer = user && (await getActiveOfficer({ req, user, ctx })); 15 | 16 | ctx.set("activeOfficer", officer); 17 | 18 | next(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /apps/client/src/components/editor/elements/leaf.tsx: -------------------------------------------------------------------------------- 1 | import type { RenderLeafProps } from "slate-react"; 2 | 3 | export function EditorLeaf({ attributes, children, leaf }: RenderLeafProps) { 4 | const style = { 5 | color: leaf["text-color"], 6 | backgroundColor: leaf["background-color"], 7 | }; 8 | 9 | const elementProps = { 10 | ...attributes, 11 | style, 12 | }; 13 | 14 | if (leaf.bold) { 15 | children = {children}; 16 | } 17 | 18 | if (leaf.italic) { 19 | children = {children}; 20 | } 21 | 22 | if (leaf.underline) { 23 | children = {children}; 24 | } 25 | 26 | if (leaf.strikethrough) { 27 | children = {children}; 28 | } 29 | 30 | return {children}; 31 | } 32 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230128075737_custom_business_roles/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "_businessValueBusinessRoles" ( 3 | "A" TEXT NOT NULL, 4 | "B" TEXT NOT NULL 5 | ); 6 | 7 | -- CreateIndex 8 | CREATE UNIQUE INDEX "_businessValueBusinessRoles_AB_unique" ON "_businessValueBusinessRoles"("A", "B"); 9 | 10 | -- CreateIndex 11 | CREATE INDEX "_businessValueBusinessRoles_B_index" ON "_businessValueBusinessRoles"("B"); 12 | 13 | -- AddForeignKey 14 | ALTER TABLE "_businessValueBusinessRoles" ADD CONSTRAINT "_businessValueBusinessRoles_A_fkey" FOREIGN KEY ("A") REFERENCES "Business"("id") ON DELETE CASCADE ON UPDATE CASCADE; 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_businessValueBusinessRoles" ADD CONSTRAINT "_businessValueBusinessRoles_B_fkey" FOREIGN KEY ("B") REFERENCES "EmployeeValue"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | -------------------------------------------------------------------------------- /apps/client/src/state/leo-state.ts: -------------------------------------------------------------------------------- 1 | import type { AssignedWarrantOfficer, BaseCitizen, Warrant } from "@snailycad/types"; 2 | import type { GetActiveOfficerData } from "@snailycad/types/api"; 3 | import { shallow } from "zustand/shallow"; 4 | import { createWithEqualityFn } from "zustand/traditional"; 5 | 6 | export type ActiveOfficer = GetActiveOfficerData; 7 | 8 | export interface ActiveWarrant extends Warrant { 9 | citizen: BaseCitizen; 10 | assignedOfficers: AssignedWarrantOfficer[]; 11 | } 12 | 13 | interface LeoState { 14 | activeOfficer: ActiveOfficer | null; 15 | setActiveOfficer(officer: ActiveOfficer | null): void; 16 | } 17 | 18 | export const useLeoState = createWithEqualityFn()( 19 | (set) => ({ 20 | activeOfficer: null, 21 | setActiveOfficer: (officer) => set({ activeOfficer: officer }), 22 | }), 23 | shallow, 24 | ); 25 | -------------------------------------------------------------------------------- /apps/api/prisma/migrations/20230120161140_address_flag_values/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterEnum 2 | ALTER TYPE "ValueType" ADD VALUE 'ADDRESS_FLAG'; 3 | 4 | -- CreateTable 5 | CREATE TABLE "_citizenAddressFlags" ( 6 | "A" TEXT NOT NULL, 7 | "B" TEXT NOT NULL 8 | ); 9 | 10 | -- CreateIndex 11 | CREATE UNIQUE INDEX "_citizenAddressFlags_AB_unique" ON "_citizenAddressFlags"("A", "B"); 12 | 13 | -- CreateIndex 14 | CREATE INDEX "_citizenAddressFlags_B_index" ON "_citizenAddressFlags"("B"); 15 | 16 | -- AddForeignKey 17 | ALTER TABLE "_citizenAddressFlags" ADD CONSTRAINT "_citizenAddressFlags_A_fkey" FOREIGN KEY ("A") REFERENCES "Citizen"("id") ON DELETE CASCADE ON UPDATE CASCADE; 18 | 19 | -- AddForeignKey 20 | ALTER TABLE "_citizenAddressFlags" ADD CONSTRAINT "_citizenAddressFlags_B_fkey" FOREIGN KEY ("B") REFERENCES "Value"("id") ON DELETE CASCADE ON UPDATE CASCADE; 21 | -------------------------------------------------------------------------------- /apps/client/src/state/search/name-search-state.ts: -------------------------------------------------------------------------------- 1 | import type { PostLeoSearchCitizenData } from "@snailycad/types/api"; 2 | import { shallow } from "zustand/shallow"; 3 | import { createWithEqualityFn } from "zustand/traditional"; 4 | 5 | export type NameSearchResult = PostLeoSearchCitizenData[number]; 6 | 7 | interface NameSearchState { 8 | results: NameSearchResult[] | null | boolean; 9 | setResults(v: NameSearchResult[] | null | boolean): void; 10 | 11 | currentResult: NameSearchResult | null; 12 | setCurrentResult(v: NameSearchResult | null): void; 13 | } 14 | 15 | export const useNameSearch = createWithEqualityFn()( 16 | (set) => ({ 17 | results: null, 18 | setResults: (v) => set({ results: v }), 19 | 20 | currentResult: null, 21 | setCurrentResult: (v) => set({ currentResult: v }), 22 | }), 23 | shallow, 24 | ); 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:20-slim AS base 2 | 3 | WORKDIR /snailycad 4 | 5 | # Install pnpm globally and set config in one layer 6 | RUN npm install -g pnpm && pnpm config set httpTimeout 1200000 7 | 8 | # Copy the rest of the source code 9 | COPY . ./ 10 | 11 | FROM base AS deps 12 | 13 | RUN pnpm install --frozen-lockfile 14 | 15 | FROM deps AS build 16 | 17 | ENV NODE_ENV="production" 18 | 19 | # Build all packages (this will also build the API and Client) 20 | RUN pnpm turbo run build --filter="{packages/*}" 21 | 22 | 23 | FROM build AS api 24 | ENV NODE_ENV="production" 25 | WORKDIR /snailycad/apps/api 26 | RUN pnpm run build 27 | CMD ["pnpm", "start"] 28 | 29 | FROM build AS client 30 | ENV NODE_ENV="production" 31 | WORKDIR /snailycad/apps/client 32 | RUN rm -rf /snailycad/apps/client/.next 33 | RUN pnpm create-images-domain 34 | RUN pnpm run build 35 | CMD ["pnpm", "start"] --------------------------------------------------------------------------------