diff --git a/MobileApp/app.json b/MobileApp/app.json index 6a7599cf83..d170cc70bd 100644 --- a/MobileApp/app.json +++ b/MobileApp/app.json @@ -6,12 +6,12 @@ "orientation": "portrait", "icon": "./assets/icon.png", "scheme": "oneuptime", - "userInterfaceStyle": "dark", + "userInterfaceStyle": "automatic", "newArchEnabled": true, "splash": { "image": "./assets/splash-icon.png", "resizeMode": "contain", - "backgroundColor": "#0D1117" + "backgroundColor": "#FFFFFF" }, "ios": { "supportsTablet": true, diff --git a/MobileApp/babel.config.js b/MobileApp/babel.config.js new file mode 100644 index 0000000000..49fb3bed45 --- /dev/null +++ b/MobileApp/babel.config.js @@ -0,0 +1,7 @@ +module.exports = function (api) { + api.cache(true); + return { + presets: ["babel-preset-expo"], + plugins: ["nativewind/babel"], + }; +}; diff --git a/MobileApp/global.css b/MobileApp/global.css new file mode 100644 index 0000000000..6c34d154b3 --- /dev/null +++ b/MobileApp/global.css @@ -0,0 +1,83 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --color-bg-primary: #FFFFFF; + --color-bg-secondary: #F9FAFB; + --color-bg-tertiary: #F3F4F6; + --color-bg-elevated: #FFFFFF; + --color-border-default: #E5E7EB; + --color-border-subtle: #F3F4F6; + --color-text-primary: #111827; + --color-text-secondary: #6B7280; + --color-text-tertiary: #9CA3AF; + --color-text-inverse: #FFFFFF; + --color-severity-critical: #CF222E; + --color-severity-critical-bg: #CF222E1A; + --color-severity-major: #BC4C00; + --color-severity-major-bg: #BC4C001A; + --color-severity-minor: #9A6700; + --color-severity-minor-bg: #9A67001A; + --color-severity-warning: #BF8700; + --color-severity-warning-bg: #BF87001A; + --color-severity-info: #0969DA; + --color-severity-info-bg: #0969DA1A; + --color-state-created: #CF222E; + --color-state-acknowledged: #9A6700; + --color-state-resolved: #1A7F37; + --color-state-investigating: #BC4C00; + --color-state-muted: #8C959F; + --color-oncall-active: #1A7F37; + --color-oncall-active-bg: #1A7F371A; + --color-oncall-inactive: #8C959F; + --color-oncall-inactive-bg: #8C959F1A; + --color-action-primary: #6366F1; + --color-action-primary-pressed: #4F46E5; + --color-action-destructive: #CF222E; + --color-action-destructive-pressed: #A40E26; + --color-status-success: #1A7F37; + --color-status-success-bg: #1A7F371A; + --color-status-error: #CF222E; + --color-status-error-bg: #CF222E1A; +} + +.dark { + --color-bg-primary: #0D1117; + --color-bg-secondary: #161B22; + --color-bg-tertiary: #21262D; + --color-bg-elevated: #1C2128; + --color-border-default: #30363D; + --color-border-subtle: #21262D; + --color-text-primary: #E6EDF3; + --color-text-secondary: #8B949E; + --color-text-tertiary: #6E7681; + --color-text-inverse: #0D1117; + --color-severity-critical: #F85149; + --color-severity-critical-bg: #F8514926; + --color-severity-major: #F0883E; + --color-severity-major-bg: #F0883E26; + --color-severity-minor: #D29922; + --color-severity-minor-bg: #D2992226; + --color-severity-warning: #E3B341; + --color-severity-warning-bg: #E3B34126; + --color-severity-info: #58A6FF; + --color-severity-info-bg: #58A6FF26; + --color-state-created: #F85149; + --color-state-acknowledged: #D29922; + --color-state-resolved: #3FB950; + --color-state-investigating: #F0883E; + --color-state-muted: #6E7681; + --color-oncall-active: #3FB950; + --color-oncall-active-bg: #3FB95026; + --color-oncall-inactive: #6E7681; + --color-oncall-inactive-bg: #6E768126; + --color-action-primary: #6366F1; + --color-action-primary-pressed: #4F46E5; + --color-action-destructive: #F85149; + --color-action-destructive-pressed: #DA3633; + --color-status-success: #3FB950; + --color-status-success-bg: #3FB95026; + --color-status-error: #F85149; + --color-status-error-bg: #F8514926; +} diff --git a/MobileApp/metro.config.js b/MobileApp/metro.config.js new file mode 100644 index 0000000000..b0963fe7f7 --- /dev/null +++ b/MobileApp/metro.config.js @@ -0,0 +1,6 @@ +const { getDefaultConfig } = require("expo/metro-config"); +const { withNativeWind } = require("nativewind/metro"); + +const config = getDefaultConfig(__dirname); + +module.exports = withNativeWind(config, { input: "./global.css" }); diff --git a/MobileApp/nativewind-env.d.ts b/MobileApp/nativewind-env.d.ts new file mode 100644 index 0000000000..c0d8380737 --- /dev/null +++ b/MobileApp/nativewind-env.d.ts @@ -0,0 +1,3 @@ +/// + +// NOTE: This file should not be edited and should be committed with your source code. It is generated by NativeWind. \ No newline at end of file diff --git a/MobileApp/package-lock.json b/MobileApp/package-lock.json index 2aa5566271..76054e808f 100644 --- a/MobileApp/package-lock.json +++ b/MobileApp/package-lock.json @@ -27,13 +27,16 @@ "expo-splash-screen": "^31.0.13", "expo-status-bar": "~3.0.9", "expo-system-ui": "~6.0.9", + "nativewind": "^4.2.1", + "postcss": "^8.5.6", "react": "19.1.0", "react-dom": "19.1.0", "react-native": "0.81.5", "react-native-keychain": "^10.0.0", "react-native-safe-area-context": "^5.6.2", "react-native-screens": "~4.16.0", - "react-native-web": "^0.21.0" + "react-native-web": "^0.21.0", + "tailwindcss": "^3.4.19" }, "devDependencies": { "@types/react": "~19.1.0", @@ -54,6 +57,18 @@ } } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", @@ -1353,6 +1368,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-typescript": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", @@ -2733,6 +2763,41 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@react-native-async-storage/async-storage": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-2.2.0.tgz", @@ -3526,6 +3591,12 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "license": "MIT" + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -3922,6 +3993,18 @@ "node": ">=0.6" } }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bplist-creator": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", @@ -4105,6 +4188,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001769", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", @@ -4139,6 +4231,42 @@ "node": ">=4" } }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", @@ -4334,6 +4462,20 @@ "node": ">= 10" } }, + "node_modules/comment-json": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.5.1.tgz", + "integrity": "sha512-taEtr3ozUmOB7it68Jll7s0Pwm+aoiHyXKrEC8SEodL4rNpdfDLqa7PfBlrgFoCNNdR8ImL+muti5IGvktJAAg==", + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -4443,6 +4585,12 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cross-fetch": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", @@ -4484,6 +4632,18 @@ "hyphenate-style-name": "^1.0.3" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -4627,6 +4787,18 @@ "node": ">=8" } }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -5457,6 +5629,34 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/expo/node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/expo/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5502,12 +5702,49 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "license": "MIT" }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", @@ -5849,6 +6086,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/glob/node_modules/minimatch": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", @@ -6151,6 +6400,18 @@ "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", "license": "MIT" }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -6193,6 +6454,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -6221,6 +6491,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-nan": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", @@ -6752,6 +7034,16 @@ "integrity": "sha512-dZ6Ra7u1G8c4Letq/B5EzAxj4tLFHL+cGtdpR+PVm4yzPDj+lCk+AbivWt1eOM+ikzkowtyV7qSqX6qr3t71Ww==", "license": "MIT" }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "peer": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7102,6 +7394,18 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -7213,6 +7517,15 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "license": "MIT" }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/metro": { "version": "0.83.3", "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.3.tgz", @@ -7713,6 +8026,23 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/nativewind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/nativewind/-/nativewind-4.2.1.tgz", + "integrity": "sha512-10uUB2Dlli3MH3NDL5nMHqJHz1A3e/E6mzjTj6cl7hHECClJ7HpE6v+xZL+GXdbwQSnWE+UWMIMsNz7yOQkAJQ==", + "license": "MIT", + "dependencies": { + "comment-json": "^4.2.5", + "debug": "^4.3.7", + "react-native-css-interop": "0.2.1" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "tailwindcss": ">3.3.0" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -7820,6 +8150,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-is": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", @@ -8109,6 +8448,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", @@ -8151,9 +8499,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -8169,8 +8517,9 @@ } ], "license": "MIT", + "peer": true, "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -8178,6 +8527,128 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -8312,6 +8783,26 @@ "inherits": "~2.0.3" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -8445,6 +8936,277 @@ } } }, + "node_modules/react-native-css-interop": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/react-native-css-interop/-/react-native-css-interop-0.2.1.tgz", + "integrity": "sha512-B88f5rIymJXmy1sNC/MhTkb3xxBej1KkuAt7TiT9iM7oXz3RM8Bn+7GUrfR02TvSgKm4cg2XiSuLEKYfKwNsjA==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.22.15", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.3.7", + "lightningcss": "~1.27.0", + "semver": "^7.6.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": ">=18", + "react-native": "*", + "react-native-reanimated": ">=3.6.2", + "tailwindcss": "~3" + }, + "peerDependenciesMeta": { + "react-native-safe-area-context": { + "optional": true + }, + "react-native-svg": { + "optional": true + } + } + }, + "node_modules/react-native-css-interop/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.27.0.tgz", + "integrity": "sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.27.0", + "lightningcss-darwin-x64": "1.27.0", + "lightningcss-freebsd-x64": "1.27.0", + "lightningcss-linux-arm-gnueabihf": "1.27.0", + "lightningcss-linux-arm64-gnu": "1.27.0", + "lightningcss-linux-arm64-musl": "1.27.0", + "lightningcss-linux-x64-gnu": "1.27.0", + "lightningcss-linux-x64-musl": "1.27.0", + "lightningcss-win32-arm64-msvc": "1.27.0", + "lightningcss-win32-x64-msvc": "1.27.0" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-darwin-arm64": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.27.0.tgz", + "integrity": "sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-darwin-x64": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.27.0.tgz", + "integrity": "sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-freebsd-x64": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.27.0.tgz", + "integrity": "sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.27.0.tgz", + "integrity": "sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.27.0.tgz", + "integrity": "sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.27.0.tgz", + "integrity": "sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.27.0.tgz", + "integrity": "sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-linux-x64-musl": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.27.0.tgz", + "integrity": "sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.27.0.tgz", + "integrity": "sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/react-native-css-interop/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.27.0.tgz", + "integrity": "sha512-/OJLj94Zm/waZShL8nB5jsNj3CfNATLCTyFxZyouilfTmSoLDX7VlVAmhPHoZWVFp4vdmoiEbPEYC8HID3m6yw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/react-native-is-edge-to-edge": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.2.1.tgz", @@ -8468,6 +9230,33 @@ "node": ">=16" } }, + "node_modules/react-native-reanimated": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.2.1.tgz", + "integrity": "sha512-/NcHnZMyOvsD/wYXug/YqSKw90P9edN0kEPL5lP4PFf1aQ4F1V7MKe/E0tvfkXKIajy3Qocp5EiEnlcrK/+BZg==", + "license": "MIT", + "dependencies": { + "react-native-is-edge-to-edge": "1.2.1", + "semver": "7.7.3" + }, + "peerDependencies": { + "react": "*", + "react-native": "*", + "react-native-worklets": ">=0.7.0" + } + }, + "node_modules/react-native-reanimated/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/react-native-safe-area-context": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", @@ -8528,6 +9317,128 @@ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", "license": "MIT" }, + "node_modules/react-native-worklets": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.7.3.tgz", + "integrity": "sha512-m/CIUCHvLQulboBn0BtgpsesXjOTeubU7t+V0lCPpBj0t2ExigwqDHoKj3ck7OeErnjgkD27wdAtQCubYATe3g==", + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-arrow-functions": "7.27.1", + "@babel/plugin-transform-class-properties": "7.27.1", + "@babel/plugin-transform-classes": "7.28.4", + "@babel/plugin-transform-nullish-coalescing-operator": "7.27.1", + "@babel/plugin-transform-optional-chaining": "7.27.1", + "@babel/plugin-transform-shorthand-properties": "7.27.1", + "@babel/plugin-transform-template-literals": "7.27.1", + "@babel/plugin-transform-unicode-regex": "7.27.1", + "@babel/preset-typescript": "7.27.1", + "convert-source-map": "2.0.0", + "semver": "7.7.3" + }, + "peerDependencies": { + "@babel/core": "*", + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-worklets/node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/react-native-worklets/node_modules/@babel/plugin-transform-classes": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", + "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/react-native-worklets/node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/react-native-worklets/node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/react-native-worklets/node_modules/@babel/preset-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", + "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/react-native-worklets/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/react-native/node_modules/@react-native/virtualized-lists": { "version": "0.81.5", "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz", @@ -8622,6 +9533,27 @@ "node": ">=0.10.0" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -8790,6 +9722,16 @@ "node": ">=4" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -8849,6 +9791,29 @@ "node": "*" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -9390,6 +10355,44 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tar": { "version": "7.5.7", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", @@ -9828,6 +10831,12 @@ "which-typed-array": "^1.1.2" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/MobileApp/package.json b/MobileApp/package.json index 69f49810f7..3965301ae7 100644 --- a/MobileApp/package.json +++ b/MobileApp/package.json @@ -29,13 +29,16 @@ "expo-splash-screen": "^31.0.13", "expo-status-bar": "~3.0.9", "expo-system-ui": "~6.0.9", + "nativewind": "^4.2.1", + "postcss": "^8.5.6", "react": "19.1.0", "react-dom": "19.1.0", "react-native": "0.81.5", "react-native-keychain": "^10.0.0", "react-native-safe-area-context": "^5.6.2", "react-native-screens": "~4.16.0", - "react-native-web": "^0.21.0" + "react-native-web": "^0.21.0", + "tailwindcss": "^3.4.19" }, "devDependencies": { "@types/react": "~19.1.0", diff --git a/MobileApp/src/App.tsx b/MobileApp/src/App.tsx index 07d2e2e874..a9abfb36c4 100644 --- a/MobileApp/src/App.tsx +++ b/MobileApp/src/App.tsx @@ -1,5 +1,6 @@ +import "../global.css"; import React from "react"; -import { View, StyleSheet, ViewStyle } from "react-native"; +import { View } from "react-native"; import { StatusBar } from "expo-status-bar"; import { QueryClient } from "@tanstack/react-query"; import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client"; @@ -29,12 +30,7 @@ function AppContent(): React.JSX.Element { const { theme } = useTheme(); return ( - + @@ -58,9 +54,3 @@ export default function App(): React.JSX.Element { ); } - -const styles: { container: ViewStyle } = StyleSheet.create({ - container: { - flex: 1, - }, -}); diff --git a/MobileApp/src/components/AddNoteModal.tsx b/MobileApp/src/components/AddNoteModal.tsx index f7ec25d2fe..635288de49 100644 --- a/MobileApp/src/components/AddNoteModal.tsx +++ b/MobileApp/src/components/AddNoteModal.tsx @@ -6,7 +6,6 @@ import { TouchableOpacity, Modal, ActivityIndicator, - StyleSheet, KeyboardAvoidingView, Platform, } from "react-native"; @@ -49,35 +48,17 @@ export default function AddNoteModal({ onRequestClose={handleClose} > - - + + Add Note - + - + Cancel @@ -130,14 +97,12 @@ export default function AddNoteModal({ /> ) : ( Submit @@ -149,41 +114,3 @@ export default function AddNoteModal({ ); } - -const styles: ReturnType = StyleSheet.create({ - overlay: { - flex: 1, - backgroundColor: "rgba(0,0,0,0.4)", - justifyContent: "flex-end", - }, - container: { - borderTopLeftRadius: 24, - borderTopRightRadius: 24, - padding: 20, - paddingBottom: 36, - }, - input: { - minHeight: 120, - borderRadius: 14, - borderWidth: 1, - padding: 12, - fontSize: 15, - }, - buttonRow: { - flexDirection: "row", - gap: 12, - marginTop: 16, - }, - button: { - flex: 1, - paddingVertical: 14, - borderRadius: 14, - alignItems: "center", - justifyContent: "center", - minHeight: 48, - }, - buttonText: { - fontSize: 15, - fontWeight: "700", - }, -}); diff --git a/MobileApp/src/components/AlertCard.tsx b/MobileApp/src/components/AlertCard.tsx index 457ba093ec..62a3b83196 100644 --- a/MobileApp/src/components/AlertCard.tsx +++ b/MobileApp/src/components/AlertCard.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { View, Text, StyleSheet, TouchableOpacity } from "react-native"; +import { View, Text, TouchableOpacity } from "react-native"; import { useTheme } from "../theme"; import { rgbToHex } from "../utils/color"; import { formatRelativeTime } from "../utils/date"; @@ -28,49 +28,34 @@ export default function AlertCard({ return ( - - + + {alert.alertNumberWithPrefix || `#${alert.alertNumber}`} - - {timeString} - + {timeString} {alert.title} - + {alert.currentAlertState ? ( - - - + + + {alert.currentAlertState.name} @@ -78,9 +63,13 @@ export default function AlertCard({ {alert.alertSeverity ? ( - + {alert.alertSeverity.name} @@ -88,61 +77,10 @@ export default function AlertCard({ {alert.monitor ? ( - + {alert.monitor.name} ) : null} ); } - -const styles: ReturnType = StyleSheet.create({ - card: { - padding: 18, - borderRadius: 16, - marginBottom: 12, - }, - topRow: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 6, - }, - number: { - fontSize: 13, - fontWeight: "600", - }, - time: { - fontSize: 12, - }, - badgeRow: { - flexDirection: "row", - flexWrap: "wrap", - gap: 8, - marginTop: 10, - }, - badge: { - flexDirection: "row", - alignItems: "center", - paddingHorizontal: 8, - paddingVertical: 4, - borderRadius: 6, - }, - dot: { - width: 8, - height: 8, - borderRadius: 4, - marginRight: 6, - }, - badgeText: { - fontSize: 12, - fontWeight: "600", - }, - monitor: { - fontSize: 12, - marginTop: 8, - }, -}); diff --git a/MobileApp/src/components/EmptyState.tsx b/MobileApp/src/components/EmptyState.tsx index 26a9caad2c..b5b3699e90 100644 --- a/MobileApp/src/components/EmptyState.tsx +++ b/MobileApp/src/components/EmptyState.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { View, Text, StyleSheet } from "react-native"; +import { View, Text } from "react-native"; import { useTheme } from "../theme"; type EmptyIcon = "incidents" | "alerts" | "episodes" | "notes" | "default"; @@ -10,22 +10,32 @@ interface EmptyStateProps { icon?: EmptyIcon; } -function EmptyIcon({ +function EmptyIconView({ icon, color, }: { icon: EmptyIcon; color: string; }): React.JSX.Element { - /* - * Simple geometric SVG-style icons using View primitives - * Monochrome, clean, professional — not cartoon/playful - */ if (icon === "incidents") { return ( - - - + + + ); @@ -33,9 +43,21 @@ function EmptyIcon({ if (icon === "alerts") { return ( - - - + + + ); @@ -43,20 +65,29 @@ function EmptyIcon({ if (icon === "episodes") { return ( - - + + ); } - // Default: simple circle with line through it return ( - - - + + + ); @@ -70,116 +101,16 @@ export default function EmptyState({ const { theme } = useTheme(); return ( - - - + + + {title} {subtitle ? ( - + {subtitle} ) : null} ); } - -const styles: ReturnType = StyleSheet.create({ - container: { - flex: 1, - alignItems: "center", - justifyContent: "center", - paddingHorizontal: 40, - paddingVertical: 80, - }, - iconContainer: { - width: 64, - height: 64, - alignItems: "center", - justifyContent: "center", - }, - // Shield icon (incidents) - iconShield: { - width: 44, - height: 52, - borderWidth: 1.5, - borderRadius: 6, - borderBottomLeftRadius: 22, - borderBottomRightRadius: 22, - alignItems: "center", - justifyContent: "center", - }, - iconCheckmark: { - width: 16, - height: 3, - borderRadius: 1.5, - transform: [{ rotate: "-45deg" }], - }, - // Bell icon (alerts) - iconBell: { - width: 36, - height: 36, - borderWidth: 1.5, - borderRadius: 18, - borderBottomLeftRadius: 4, - borderBottomRightRadius: 4, - alignItems: "center", - justifyContent: "flex-end", - paddingBottom: 4, - }, - iconBellClapper: { - width: 8, - height: 8, - borderRadius: 4, - }, - // Stack icon (episodes) - iconStack: { - width: 40, - height: 32, - borderWidth: 1.5, - borderRadius: 8, - position: "absolute", - top: 12, - }, - iconStackBack: { - width: 32, - height: 28, - borderWidth: 1.5, - borderRadius: 6, - position: "absolute", - top: 6, - }, - // Default circle icon - iconCircle: { - width: 48, - height: 48, - borderWidth: 1.5, - borderRadius: 24, - alignItems: "center", - justifyContent: "center", - }, - iconLine: { - width: 20, - height: 2, - borderRadius: 1, - }, -}); diff --git a/MobileApp/src/components/EpisodeCard.tsx b/MobileApp/src/components/EpisodeCard.tsx index 22a75ea8fe..f0d84ae611 100644 --- a/MobileApp/src/components/EpisodeCard.tsx +++ b/MobileApp/src/components/EpisodeCard.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { View, Text, StyleSheet, TouchableOpacity } from "react-native"; +import { View, Text, TouchableOpacity } from "react-native"; import { useTheme } from "../theme"; import { rgbToHex } from "../utils/color"; import { formatRelativeTime } from "../utils/date"; @@ -56,47 +56,32 @@ export default function EpisodeCard( return ( - - + + {episode.episodeNumberWithPrefix || `#${episode.episodeNumber}`} - - {timeString} - + {timeString} {episode.title} - + {state ? ( - - - + + + {state.name} @@ -104,9 +89,13 @@ export default function EpisodeCard( {severity ? ( - + {severity.name} @@ -114,9 +103,7 @@ export default function EpisodeCard( {childCount > 0 ? ( - + {childCount} {type === "incident" ? "incident" : "alert"} {childCount !== 1 ? "s" : ""} @@ -124,51 +111,3 @@ export default function EpisodeCard( ); } - -const styles: ReturnType = StyleSheet.create({ - card: { - padding: 18, - borderRadius: 16, - marginBottom: 12, - }, - topRow: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 6, - }, - number: { - fontSize: 13, - fontWeight: "600", - }, - time: { - fontSize: 12, - }, - badgeRow: { - flexDirection: "row", - flexWrap: "wrap", - gap: 8, - marginTop: 10, - }, - badge: { - flexDirection: "row", - alignItems: "center", - paddingHorizontal: 8, - paddingVertical: 4, - borderRadius: 6, - }, - dot: { - width: 8, - height: 8, - borderRadius: 4, - marginRight: 6, - }, - badgeText: { - fontSize: 12, - fontWeight: "600", - }, - childCount: { - fontSize: 12, - marginTop: 8, - }, -}); diff --git a/MobileApp/src/components/IncidentCard.tsx b/MobileApp/src/components/IncidentCard.tsx index 2f7026007f..10a8117155 100644 --- a/MobileApp/src/components/IncidentCard.tsx +++ b/MobileApp/src/components/IncidentCard.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { View, Text, StyleSheet, TouchableOpacity } from "react-native"; +import { View, Text, TouchableOpacity } from "react-native"; import { useTheme } from "../theme"; import { rgbToHex } from "../utils/color"; import { formatRelativeTime } from "../utils/date"; @@ -31,49 +31,34 @@ export default function IncidentCard({ return ( - - + + {incident.incidentNumberWithPrefix || `#${incident.incidentNumber}`} - - {timeString} - + {timeString} {incident.title} - + {incident.currentIncidentState ? ( - - - + + + {incident.currentIncidentState.name} @@ -81,9 +66,13 @@ export default function IncidentCard({ {incident.incidentSeverity ? ( - + {incident.incidentSeverity.name} @@ -91,10 +80,7 @@ export default function IncidentCard({ {monitorCount > 0 ? ( - + {incident.monitors .map((m: NamedEntity) => { return m.name; @@ -105,51 +91,3 @@ export default function IncidentCard({ ); } - -const styles: ReturnType = StyleSheet.create({ - card: { - padding: 18, - borderRadius: 16, - marginBottom: 12, - }, - topRow: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 6, - }, - number: { - fontSize: 13, - fontWeight: "600", - }, - time: { - fontSize: 12, - }, - badgeRow: { - flexDirection: "row", - flexWrap: "wrap", - gap: 8, - marginTop: 10, - }, - badge: { - flexDirection: "row", - alignItems: "center", - paddingHorizontal: 8, - paddingVertical: 4, - borderRadius: 6, - }, - dot: { - width: 8, - height: 8, - borderRadius: 4, - marginRight: 6, - }, - badgeText: { - fontSize: 12, - fontWeight: "600", - }, - monitors: { - fontSize: 12, - marginTop: 8, - }, -}); diff --git a/MobileApp/src/components/OfflineBanner.tsx b/MobileApp/src/components/OfflineBanner.tsx index 690f315e01..4479d293ee 100644 --- a/MobileApp/src/components/OfflineBanner.tsx +++ b/MobileApp/src/components/OfflineBanner.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef } from "react"; -import { View, Text, StyleSheet, Animated } from "react-native"; +import { View, Text, Animated } from "react-native"; import { useTheme } from "../theme"; import { useNetworkStatus } from "../hooks/useNetworkStatus"; @@ -25,51 +25,18 @@ export default function OfflineBanner(): React.JSX.Element | null { return ( - - - + + + No internet connection ); } - -const styles: ReturnType = StyleSheet.create({ - container: { - position: "absolute", - top: 0, - left: 0, - right: 0, - zIndex: 100, - paddingTop: 50, - paddingBottom: 8, - paddingHorizontal: 16, - }, - content: { - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - }, - dot: { - width: 6, - height: 6, - borderRadius: 3, - backgroundColor: "#FFFFFF", - marginRight: 8, - opacity: 0.8, - }, - text: { - fontSize: 13, - fontWeight: "600", - letterSpacing: 0.2, - }, -}); diff --git a/MobileApp/src/components/ProjectBadge.tsx b/MobileApp/src/components/ProjectBadge.tsx index 9799c940a8..7513f3ea56 100644 --- a/MobileApp/src/components/ProjectBadge.tsx +++ b/MobileApp/src/components/ProjectBadge.tsx @@ -1,6 +1,5 @@ import React from "react"; -import { View, Text, StyleSheet } from "react-native"; -import { useTheme } from "../theme"; +import { View, Text } from "react-native"; interface ProjectBadgeProps { name: string; @@ -11,35 +10,15 @@ export default function ProjectBadge({ name, color, }: ProjectBadgeProps): React.JSX.Element { - const { theme } = useTheme(); - - const dotColor: string = color || theme.colors.actionPrimary; - return ( - - - + + + {name} ); } - -const styles: ReturnType = StyleSheet.create({ - container: { - flexDirection: "row", - alignItems: "center", - }, - dot: { - width: 8, - height: 8, - borderRadius: 4, - marginRight: 6, - }, -}); diff --git a/MobileApp/src/components/SeverityBadge.tsx b/MobileApp/src/components/SeverityBadge.tsx index 1d487abc29..6b5d0d8139 100644 --- a/MobileApp/src/components/SeverityBadge.tsx +++ b/MobileApp/src/components/SeverityBadge.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { View, Text, StyleSheet } from "react-native"; +import { View, Text } from "react-native"; import { useTheme } from "../theme"; export type SeverityLevel = "critical" | "major" | "minor" | "warning" | "info"; @@ -42,24 +42,16 @@ export default function SeverityBadge({ const displayLabel: string = label || severity; return ( - - + + {displayLabel.toUpperCase()} ); } - -const styles: ReturnType = StyleSheet.create({ - badge: { - paddingHorizontal: 8, - paddingVertical: 4, - borderRadius: 6, - alignSelf: "flex-start", - }, - text: { - fontSize: 12, - fontWeight: "600", - letterSpacing: 0.5, - }, -}); diff --git a/MobileApp/src/components/SkeletonCard.tsx b/MobileApp/src/components/SkeletonCard.tsx index e5c2b35fde..bef6b6578b 100644 --- a/MobileApp/src/components/SkeletonCard.tsx +++ b/MobileApp/src/components/SkeletonCard.tsx @@ -1,11 +1,5 @@ import React, { useEffect, useRef } from "react"; -import { - View, - StyleSheet, - Animated, - DimensionValue, - AccessibilityInfo, -} from "react-native"; +import { View, Animated, DimensionValue, AccessibilityInfo } from "react-native"; import { useTheme } from "../theme"; interface SkeletonCardProps { @@ -60,36 +54,19 @@ export default function SkeletonCard({ if (variant === "compact") { return ( - - - + + + - + ); } @@ -97,57 +74,28 @@ export default function SkeletonCard({ if (variant === "detail") { return ( - {/* Badge row */} - - - + + + - {/* Title */} + - {/* Detail card */} - {Array.from({ length: 3 }).map((_: unknown, index: number) => { return ( - - - + + + ); })} @@ -158,67 +106,31 @@ export default function SkeletonCard({ return ( - {/* Top row: badge + time */} - - - + + + - {/* Title */} - - {/* Badge row */} - - - + + + + - {/* Body lines */} {Array.from({ length: Math.max(lines - 1, 1) }).map( (_: unknown, index: number) => { return ( ); }, @@ -226,94 +138,3 @@ export default function SkeletonCard({ ); } - -const styles: ReturnType = StyleSheet.create({ - card: { - padding: 18, - borderRadius: 16, - marginBottom: 12, - }, - topRow: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 12, - }, - numberSkeleton: { - height: 14, - width: 48, - borderRadius: 4, - }, - timeSkeleton: { - height: 12, - width: 36, - borderRadius: 4, - }, - titleLine: { - height: 18, - borderRadius: 4, - width: "70%", - marginBottom: 12, - }, - badgeRow: { - flexDirection: "row", - gap: 8, - marginBottom: 12, - }, - badgeSkeleton: { - height: 24, - width: 80, - borderRadius: 6, - }, - line: { - height: 12, - borderRadius: 4, - marginBottom: 8, - }, - // Compact variant - compactRow: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 10, - }, - compactBadge: { - height: 14, - width: 48, - borderRadius: 4, - }, - compactTime: { - height: 12, - width: 32, - borderRadius: 4, - }, - // Detail variant - detailContainer: { - padding: 20, - }, - detailTitle: { - height: 24, - width: "80%", - borderRadius: 4, - marginBottom: 20, - }, - detailCard: { - borderRadius: 16, - padding: 16, - }, - detailRow: { - flexDirection: "row", - marginBottom: 12, - }, - detailLabel: { - height: 14, - width: 80, - borderRadius: 4, - marginRight: 16, - }, - detailValue: { - height: 14, - width: 120, - borderRadius: 4, - }, -}); diff --git a/MobileApp/src/components/StateBadge.tsx b/MobileApp/src/components/StateBadge.tsx index bbfb609813..5878452b56 100644 --- a/MobileApp/src/components/StateBadge.tsx +++ b/MobileApp/src/components/StateBadge.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { View, Text, StyleSheet } from "react-native"; +import { View, Text } from "react-native"; import { useTheme } from "../theme"; export type StateType = @@ -32,39 +32,14 @@ export default function StateBadge({ const displayLabel: string = label || state; return ( - - - + + + {displayLabel.charAt(0).toUpperCase() + displayLabel.slice(1)} ); } - -const styles: ReturnType = StyleSheet.create({ - badge: { - flexDirection: "row", - alignItems: "center", - paddingHorizontal: 8, - paddingVertical: 4, - borderRadius: 6, - alignSelf: "flex-start", - }, - dot: { - width: 8, - height: 8, - borderRadius: 4, - marginRight: 6, - }, - text: { - fontSize: 12, - fontWeight: "600", - }, -}); diff --git a/MobileApp/src/components/SwipeableCard.tsx b/MobileApp/src/components/SwipeableCard.tsx index ff348c4223..fe90a69196 100644 --- a/MobileApp/src/components/SwipeableCard.tsx +++ b/MobileApp/src/components/SwipeableCard.tsx @@ -2,7 +2,6 @@ import React, { useRef } from "react"; import { View, Text, - StyleSheet, Animated, PanResponder, type GestureResponderEvent, @@ -48,7 +47,6 @@ export default function SwipeableCard({ _: GestureResponderEvent, gestureState: PanResponderGestureState, ) => { - // Limit swipe range const maxSwipe: number = 120; let dx: number = gestureState.dx; if (!rightAction && dx < 0) { @@ -60,7 +58,6 @@ export default function SwipeableCard({ dx = Math.max(-maxSwipe, Math.min(maxSwipe, dx)); translateX.setValue(dx); - // Haptic feedback at threshold if (Math.abs(dx) >= SWIPE_THRESHOLD && !hasTriggeredHaptic.current) { hasTriggeredHaptic.current = true; mediumImpact(); @@ -97,34 +94,35 @@ export default function SwipeableCard({ ).current; return ( - + {/* Background actions */} - + {leftAction ? ( - {leftAction.label} + + {leftAction.label} + ) : null} {rightAction ? ( - {rightAction.label} + + {rightAction.label} + ) : null} {/* Foreground content */} {children} @@ -132,40 +130,3 @@ export default function SwipeableCard({ ); } - -const styles: ReturnType = StyleSheet.create({ - container: { - overflow: "hidden", - borderRadius: 12, - }, - actionsContainer: { - ...StyleSheet.absoluteFillObject, - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - }, - actionLeft: { - flex: 1, - height: "100%", - justifyContent: "center", - paddingLeft: 20, - borderRadius: 12, - }, - actionRight: { - flex: 1, - height: "100%", - justifyContent: "center", - alignItems: "flex-end", - paddingRight: 20, - borderRadius: 12, - }, - actionText: { - color: "#FFFFFF", - fontSize: 14, - fontWeight: "700", - letterSpacing: 0.3, - }, - foreground: { - zIndex: 1, - }, -}); diff --git a/MobileApp/src/navigation/RootNavigator.tsx b/MobileApp/src/navigation/RootNavigator.tsx index b29a3186f4..980eba0fa5 100644 --- a/MobileApp/src/navigation/RootNavigator.tsx +++ b/MobileApp/src/navigation/RootNavigator.tsx @@ -15,7 +15,7 @@ import AuthStackNavigator from "./AuthStackNavigator"; import MainTabNavigator from "./MainTabNavigator"; import ProjectSelectionScreen from "../screens/ProjectSelectionScreen"; import BiometricLockScreen from "../screens/BiometricLockScreen"; -import { View, ActivityIndicator, StyleSheet } from "react-native"; +import { View, ActivityIndicator } from "react-native"; const prefix: string = Linking.createURL("/"); @@ -96,12 +96,7 @@ export default function RootNavigator(): React.JSX.Element { if (isLoading || !biometricChecked) { return ( - + ); @@ -130,12 +125,7 @@ export default function RootNavigator(): React.JSX.Element { if (isLoadingProjects) { return ( - + ); @@ -158,11 +148,3 @@ export default function RootNavigator(): React.JSX.Element { ); } - -const styles: ReturnType = StyleSheet.create({ - loading: { - flex: 1, - alignItems: "center", - justifyContent: "center", - }, -}); diff --git a/MobileApp/src/screens/AlertDetailScreen.tsx b/MobileApp/src/screens/AlertDetailScreen.tsx index a1cef38c7a..a3449da4f2 100644 --- a/MobileApp/src/screens/AlertDetailScreen.tsx +++ b/MobileApp/src/screens/AlertDetailScreen.tsx @@ -7,7 +7,6 @@ import { ActivityIndicator, RefreshControl, Alert, - StyleSheet, } from "react-native"; import type { NativeStackScreenProps } from "@react-navigation/native-stack"; import { useTheme } from "../theme"; @@ -128,9 +127,7 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { if (isLoading) { return ( - + ); @@ -138,18 +135,8 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { if (!alert) { return ( - - + + Alert not found. @@ -180,46 +167,32 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { return ( } > {/* Header */} - - + + {alert.alertNumberWithPrefix || `#${alert.alertNumber}`} - + {alert.title} {/* Badges */} - + {alert.currentAlertState ? ( - - - + + + {alert.currentAlertState.name} @@ -227,9 +200,13 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { {alert.alertSeverity ? ( - + {alert.alertSeverity.name} @@ -238,69 +215,38 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { {/* Description */} {alert.description ? ( - - + + Description - + {alert.description} ) : null} {/* Details */} - - + + Details - - - + + + Created - + {formatDateTime(alert.createdAt)} {alert.monitor ? ( - - + + Monitor - + {alert.monitor.name} @@ -310,20 +256,15 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { {/* State Change Actions */} {!isResolved ? ( - - + + Actions - + {!isAcknowledged && !isResolved && acknowledgeState ? ( { return handleStateChange( acknowledgeState._id, @@ -340,12 +281,7 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { color={theme.colors.textInverse} /> ) : ( - + Acknowledge )} @@ -354,11 +290,8 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { {resolveState ? ( { return handleStateChange(resolveState._id, resolveState.name); }} @@ -372,12 +305,7 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { color={theme.colors.textInverse} /> ) : ( - + Resolve )} @@ -389,10 +317,8 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { {/* State Timeline */} {timeline && timeline.length > 0 ? ( - - + + State Timeline {timeline.map((entry: StateTimelineItem) => { @@ -402,32 +328,17 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { return ( - - + + {entry.alertState?.name ?? "Unknown"} - + {formatDateTime(entry.createdAt)} @@ -438,31 +349,19 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { ) : null} {/* Internal Notes */} - - - + + + Internal Notes { return setNoteModalVisible(true); }} > - + Add Note @@ -473,39 +372,18 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { return ( - + {note.note} - + {note.createdByUser ? ( - + {note.createdByUser.name} ) : null} - + {formatDateTime(note.createdAt)} @@ -515,12 +393,7 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { : null} {notes && notes.length === 0 ? ( - + No notes yet. ) : null} @@ -537,130 +410,3 @@ export default function AlertDetailScreen({ route }: Props): React.JSX.Element { ); } - -const styles: ReturnType = StyleSheet.create({ - centered: { - flex: 1, - alignItems: "center", - justifyContent: "center", - }, - content: { - padding: 20, - paddingBottom: 40, - }, - numberBadge: { - alignSelf: "flex-start", - paddingHorizontal: 10, - paddingVertical: 4, - borderRadius: 8, - }, - number: { - fontSize: 14, - fontWeight: "600", - }, - badgeRow: { - flexDirection: "row", - flexWrap: "wrap", - gap: 8, - marginTop: 12, - }, - badge: { - flexDirection: "row", - alignItems: "center", - paddingHorizontal: 10, - paddingVertical: 5, - borderRadius: 6, - }, - dot: { - width: 8, - height: 8, - borderRadius: 4, - marginRight: 6, - }, - badgeText: { - fontSize: 13, - fontWeight: "600", - }, - section: { - marginTop: 24, - }, - sectionHeader: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 10, - }, - sectionTitle: { - fontSize: 13, - fontWeight: "600", - textTransform: "uppercase", - letterSpacing: 0.5, - marginBottom: 10, - }, - detailCard: { - borderRadius: 16, - padding: 16, - }, - detailRow: { - flexDirection: "row", - marginBottom: 10, - }, - detailLabel: { - fontSize: 14, - width: 90, - }, - detailValue: { - fontSize: 14, - }, - actionRow: { - flexDirection: "row", - gap: 12, - }, - actionButton: { - flex: 1, - paddingVertical: 14, - borderRadius: 14, - alignItems: "center", - justifyContent: "center", - minHeight: 50, - }, - actionButtonText: { - fontSize: 15, - fontWeight: "700", - }, - timelineEntry: { - flexDirection: "row", - alignItems: "center", - padding: 14, - borderRadius: 12, - marginBottom: 8, - }, - timelineDot: { - width: 10, - height: 10, - borderRadius: 5, - marginRight: 12, - }, - timelineInfo: { - flex: 1, - }, - addNoteButton: { - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 8, - }, - addNoteButtonText: { - fontSize: 13, - fontWeight: "600", - }, - noteCard: { - borderRadius: 12, - padding: 14, - marginBottom: 8, - }, - noteMeta: { - flexDirection: "row", - justifyContent: "space-between", - marginTop: 8, - }, -}); diff --git a/MobileApp/src/screens/AlertEpisodeDetailScreen.tsx b/MobileApp/src/screens/AlertEpisodeDetailScreen.tsx index e4f4cd15d6..5bed44c671 100644 --- a/MobileApp/src/screens/AlertEpisodeDetailScreen.tsx +++ b/MobileApp/src/screens/AlertEpisodeDetailScreen.tsx @@ -7,7 +7,6 @@ import { ActivityIndicator, RefreshControl, Alert, - StyleSheet, } from "react-native"; import type { NativeStackScreenProps } from "@react-navigation/native-stack"; import { useTheme } from "../theme"; @@ -135,9 +134,7 @@ export default function AlertEpisodeDetailScreen({ if (isLoading) { return ( - + ); @@ -145,18 +142,8 @@ export default function AlertEpisodeDetailScreen({ if (!episode) { return ( - - + + Episode not found. @@ -186,46 +173,32 @@ export default function AlertEpisodeDetailScreen({ return ( } > {/* Header */} - - + + {episode.episodeNumberWithPrefix || `#${episode.episodeNumber}`} - + {episode.title} {/* Badges */} - + {episode.currentAlertState ? ( - - - + + + {episode.currentAlertState.name} @@ -233,9 +206,13 @@ export default function AlertEpisodeDetailScreen({ {episode.alertSeverity ? ( - + {episode.alertSeverity.name} @@ -244,62 +221,37 @@ export default function AlertEpisodeDetailScreen({ {/* Description */} {episode.description ? ( - - + + Description - + {episode.description} ) : null} {/* Details */} - - + + Details - - - + + + Created - + {formatDateTime(episode.createdAt)} - - + + Alerts - + {episode.alertCount ?? 0} @@ -308,20 +260,15 @@ export default function AlertEpisodeDetailScreen({ {/* State Change Actions */} {!isResolved ? ( - - + + Actions - + {!isAcknowledged && !isResolved && acknowledgeState ? ( { return handleStateChange( acknowledgeState._id, @@ -336,12 +283,7 @@ export default function AlertEpisodeDetailScreen({ color={theme.colors.textInverse} /> ) : ( - + Acknowledge )} @@ -350,11 +292,8 @@ export default function AlertEpisodeDetailScreen({ {resolveState ? ( { return handleStateChange(resolveState._id, resolveState.name); }} @@ -366,12 +305,7 @@ export default function AlertEpisodeDetailScreen({ color={theme.colors.textInverse} /> ) : ( - + Resolve )} @@ -383,10 +317,8 @@ export default function AlertEpisodeDetailScreen({ {/* State Timeline */} {timeline && timeline.length > 0 ? ( - - + + State Timeline {timeline.map((entry: StateTimelineItem) => { @@ -396,35 +328,17 @@ export default function AlertEpisodeDetailScreen({ return ( - - + + {entry.alertState?.name ?? "Unknown"} - + {formatDateTime(entry.createdAt)} @@ -435,31 +349,19 @@ export default function AlertEpisodeDetailScreen({ ) : null} {/* Internal Notes */} - - - + + + Internal Notes { return setNoteModalVisible(true); }} > - + Add Note @@ -470,39 +372,18 @@ export default function AlertEpisodeDetailScreen({ return ( - + {note.note} - + {note.createdByUser ? ( - + {note.createdByUser.name} ) : null} - + {formatDateTime(note.createdAt)} @@ -512,12 +393,7 @@ export default function AlertEpisodeDetailScreen({ : null} {notes && notes.length === 0 ? ( - + No notes yet. ) : null} @@ -534,130 +410,3 @@ export default function AlertEpisodeDetailScreen({ ); } - -const styles: ReturnType = StyleSheet.create({ - centered: { - flex: 1, - alignItems: "center", - justifyContent: "center", - }, - content: { - padding: 20, - paddingBottom: 40, - }, - numberBadge: { - alignSelf: "flex-start", - paddingHorizontal: 10, - paddingVertical: 4, - borderRadius: 8, - }, - number: { - fontSize: 14, - fontWeight: "600", - }, - badgeRow: { - flexDirection: "row", - flexWrap: "wrap", - gap: 8, - marginTop: 12, - }, - badge: { - flexDirection: "row", - alignItems: "center", - paddingHorizontal: 10, - paddingVertical: 5, - borderRadius: 6, - }, - dot: { - width: 8, - height: 8, - borderRadius: 4, - marginRight: 6, - }, - badgeText: { - fontSize: 13, - fontWeight: "600", - }, - section: { - marginTop: 24, - }, - sectionHeader: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 10, - }, - sectionTitle: { - fontSize: 13, - fontWeight: "600", - textTransform: "uppercase", - letterSpacing: 0.5, - marginBottom: 10, - }, - detailCard: { - borderRadius: 16, - padding: 16, - }, - detailRow: { - flexDirection: "row", - marginBottom: 10, - }, - detailLabel: { - fontSize: 14, - width: 90, - }, - detailValue: { - fontSize: 14, - }, - actionRow: { - flexDirection: "row", - gap: 12, - }, - actionButton: { - flex: 1, - paddingVertical: 14, - borderRadius: 14, - alignItems: "center", - justifyContent: "center", - minHeight: 50, - }, - actionButtonText: { - fontSize: 15, - fontWeight: "700", - }, - timelineEntry: { - flexDirection: "row", - alignItems: "center", - padding: 14, - borderRadius: 12, - marginBottom: 8, - }, - timelineDot: { - width: 10, - height: 10, - borderRadius: 5, - marginRight: 12, - }, - timelineInfo: { - flex: 1, - }, - addNoteButton: { - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 8, - }, - addNoteButtonText: { - fontSize: 13, - fontWeight: "600", - }, - noteCard: { - borderRadius: 12, - padding: 14, - marginBottom: 8, - }, - noteMeta: { - flexDirection: "row", - justifyContent: "space-between", - marginTop: 8, - }, -}); diff --git a/MobileApp/src/screens/AlertEpisodesScreen.tsx b/MobileApp/src/screens/AlertEpisodesScreen.tsx index 64327d3bf7..9c55bc08c8 100644 --- a/MobileApp/src/screens/AlertEpisodesScreen.tsx +++ b/MobileApp/src/screens/AlertEpisodesScreen.tsx @@ -5,7 +5,6 @@ import { RefreshControl, TouchableOpacity, Text, - StyleSheet, ListRenderItemInfo, } from "react-native"; import { useNavigation } from "@react-navigation/native"; @@ -72,13 +71,8 @@ export default function AlertEpisodesScreen(): React.JSX.Element { if (isLoading && episodes.length === 0) { return ( - - + + @@ -89,36 +83,18 @@ export default function AlertEpisodesScreen(): React.JSX.Element { if (isError) { return ( - - + + Failed to load alert episodes. { return refetch(); }} > - + Retry @@ -127,19 +103,14 @@ export default function AlertEpisodesScreen(): React.JSX.Element { } return ( - + { return item._id; }} contentContainerStyle={ - episodes.length === 0 ? styles.emptyContainer : styles.list + episodes.length === 0 ? { flex: 1 } : { padding: 16 } } renderItem={({ item }: ListRenderItemInfo) => { return ( @@ -168,30 +139,3 @@ export default function AlertEpisodesScreen(): React.JSX.Element { ); } - -const styles: ReturnType = StyleSheet.create({ - container: { - flex: 1, - }, - centered: { - flex: 1, - alignItems: "center", - justifyContent: "center", - paddingHorizontal: 32, - }, - list: { - padding: 16, - }, - emptyContainer: { - flex: 1, - }, - skeletonList: { - padding: 16, - }, - retryButton: { - marginTop: 16, - paddingHorizontal: 24, - paddingVertical: 12, - borderRadius: 10, - }, -}); diff --git a/MobileApp/src/screens/AlertsScreen.tsx b/MobileApp/src/screens/AlertsScreen.tsx index 03ffb6529c..58d746a1ac 100644 --- a/MobileApp/src/screens/AlertsScreen.tsx +++ b/MobileApp/src/screens/AlertsScreen.tsx @@ -5,7 +5,6 @@ import { RefreshControl, TouchableOpacity, Text, - StyleSheet, ListRenderItemInfo, } from "react-native"; import { useNavigation } from "@react-navigation/native"; @@ -103,13 +102,8 @@ export default function AlertsScreen(): React.JSX.Element { if (isLoading && alerts.length === 0) { return ( - - + + @@ -120,36 +114,18 @@ export default function AlertsScreen(): React.JSX.Element { if (isError) { return ( - - + + Failed to load alerts. { return refetch(); }} > - + Retry @@ -158,19 +134,14 @@ export default function AlertsScreen(): React.JSX.Element { } return ( - + { return item._id; }} contentContainerStyle={ - alerts.length === 0 ? styles.emptyContainer : styles.list + alerts.length === 0 ? { flex: 1 } : { padding: 16 } } renderItem={({ item }: ListRenderItemInfo) => { return ( @@ -213,30 +184,3 @@ export default function AlertsScreen(): React.JSX.Element { ); } - -const styles: ReturnType = StyleSheet.create({ - container: { - flex: 1, - }, - centered: { - flex: 1, - alignItems: "center", - justifyContent: "center", - paddingHorizontal: 32, - }, - list: { - padding: 16, - }, - emptyContainer: { - flex: 1, - }, - skeletonList: { - padding: 16, - }, - retryButton: { - marginTop: 16, - paddingHorizontal: 24, - paddingVertical: 12, - borderRadius: 10, - }, -}); diff --git a/MobileApp/src/screens/BiometricLockScreen.tsx b/MobileApp/src/screens/BiometricLockScreen.tsx index d26022d042..ec1f13320d 100644 --- a/MobileApp/src/screens/BiometricLockScreen.tsx +++ b/MobileApp/src/screens/BiometricLockScreen.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import { View, Text, TouchableOpacity, StyleSheet } from "react-native"; +import { View, Text, TouchableOpacity } from "react-native"; import { useTheme } from "../theme"; import * as LocalAuthentication from "expo-local-authentication"; @@ -32,117 +32,44 @@ export default function BiometricLockScreen({ }, []); return ( - + {/* Lock icon */} - + OneUptime is Locked - + Use {biometricType.toLowerCase()} to unlock - - Unlock - + Unlock ); } - -const styles: ReturnType = StyleSheet.create({ - container: { - flex: 1, - alignItems: "center", - justifyContent: "center", - paddingHorizontal: 40, - }, - iconContainer: { - width: 80, - height: 80, - borderRadius: 40, - borderWidth: 1.5, - alignItems: "center", - justifyContent: "center", - }, - lockBody: { - width: 28, - height: 22, - borderRadius: 4, - marginTop: 8, - }, - lockShackle: { - width: 20, - height: 16, - borderWidth: 3, - borderBottomWidth: 0, - borderTopLeftRadius: 10, - borderTopRightRadius: 10, - position: "absolute", - top: 16, - }, - unlockButton: { - marginTop: 32, - paddingVertical: 16, - paddingHorizontal: 48, - borderRadius: 14, - minWidth: 200, - alignItems: "center", - }, - unlockButtonText: { - fontSize: 17, - fontWeight: "600", - }, -}); diff --git a/MobileApp/src/screens/HomeScreen.tsx b/MobileApp/src/screens/HomeScreen.tsx index 5190f41040..70e909a4c9 100644 --- a/MobileApp/src/screens/HomeScreen.tsx +++ b/MobileApp/src/screens/HomeScreen.tsx @@ -5,7 +5,6 @@ import { TouchableOpacity, ScrollView, RefreshControl, - StyleSheet, } from "react-native"; import { useTheme } from "../theme"; import { useProject } from "../hooks/useProject"; @@ -35,7 +34,6 @@ function StatCard({ isLoading, onPress, }: StatCardProps): React.JSX.Element { - const { theme } = useTheme(); const { lightImpact } = useHaptics(); const handlePress: () => void = (): void => { @@ -45,22 +43,19 @@ function StatCard({ return ( - + {isLoading ? "--" : count ?? 0} - + {label} @@ -73,27 +68,15 @@ interface QuickLinkProps { } function QuickLink({ label, onPress }: QuickLinkProps): React.JSX.Element { - const { theme } = useTheme(); - return ( - - {label} - - - › - + {label} + {">"} ); } @@ -142,8 +125,8 @@ export default function HomeScreen(): React.JSX.Element { return ( } > - {/* Header */} - + {selectedProject?.name ?? "OneUptime"} - + Project overview - {/* Stats Grid */} - + - + - {/* Quick Links */} - - + + Quick Links ); } - -const styles: ReturnType = StyleSheet.create({ - content: { - padding: 24, - paddingBottom: 40, - }, - cardRow: { - flexDirection: "row", - gap: 12, - marginTop: 16, - }, - summaryCard: { - flex: 1, - padding: 20, - borderRadius: 16, - alignItems: "center", - }, - cardCount: { - fontSize: 40, - fontWeight: "700", - fontVariant: ["tabular-nums"], - }, - cardLabel: { - fontSize: 14, - fontWeight: "500", - marginTop: 4, - }, - quickLinksSection: { - marginTop: 32, - }, - sectionTitle: { - fontSize: 13, - fontWeight: "600", - textTransform: "uppercase", - letterSpacing: 0.8, - marginBottom: 12, - marginLeft: 4, - }, - linkCard: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - padding: 18, - borderRadius: 16, - marginBottom: 10, - }, - linkLabel: { - fontSize: 16, - fontWeight: "500", - }, - chevron: { - fontSize: 24, - fontWeight: "300", - }, -}); diff --git a/MobileApp/src/screens/IncidentDetailScreen.tsx b/MobileApp/src/screens/IncidentDetailScreen.tsx index e26644b93b..fa2b664e23 100644 --- a/MobileApp/src/screens/IncidentDetailScreen.tsx +++ b/MobileApp/src/screens/IncidentDetailScreen.tsx @@ -7,10 +7,9 @@ import { ActivityIndicator, RefreshControl, Alert, - StyleSheet, } from "react-native"; import type { NativeStackScreenProps } from "@react-navigation/native-stack"; -import { useTheme, type Theme } from "../theme"; +import { useTheme } from "../theme"; import { useProject } from "../hooks/useProject"; import { useIncidentDetail, @@ -41,7 +40,7 @@ export default function IncidentDetailScreen({ route, }: Props): React.JSX.Element { const { incidentId } = route.params; - const { theme }: { theme: Theme } = useTheme(); + const { theme } = useTheme(); const { selectedProject } = useProject(); const projectId: string = selectedProject?._id ?? ""; const queryClient: QueryClient = useQueryClient(); @@ -139,9 +138,7 @@ export default function IncidentDetailScreen({ if (isLoading) { return ( - + ); @@ -149,18 +146,8 @@ export default function IncidentDetailScreen({ if (!incident) { return ( - - + + Incident not found. @@ -193,46 +180,32 @@ export default function IncidentDetailScreen({ return ( } > {/* Header */} - - + + {incident.incidentNumberWithPrefix || `#${incident.incidentNumber}`} - + {incident.title} {/* Badges */} - + {incident.currentIncidentState ? ( - - - + + + {incident.currentIncidentState.name} @@ -240,9 +213,13 @@ export default function IncidentDetailScreen({ {incident.incidentSeverity ? ( - + {incident.incidentSeverity.name} @@ -251,90 +228,49 @@ export default function IncidentDetailScreen({ {/* Description */} {incident.description ? ( - - + + Description - + {incident.description} ) : null} {/* Details */} - - + + Details - + {incident.declaredAt ? ( - - + + Declared - + {formatDateTime(incident.declaredAt)} ) : null} - - + + Created - + {formatDateTime(incident.createdAt)} {incident.monitors?.length > 0 ? ( - - + + Monitors - + {incident.monitors .map((m: NamedEntity) => { return m.name; @@ -348,20 +284,15 @@ export default function IncidentDetailScreen({ {/* State Change Actions */} {!isResolved ? ( - - + + Actions - + {!isAcknowledged && !isResolved && acknowledgeState ? ( { return handleStateChange( acknowledgeState._id, @@ -378,12 +309,7 @@ export default function IncidentDetailScreen({ color={theme.colors.textInverse} /> ) : ( - + Acknowledge )} @@ -392,11 +318,8 @@ export default function IncidentDetailScreen({ {resolveState ? ( { return handleStateChange(resolveState._id, resolveState.name); }} @@ -410,12 +333,7 @@ export default function IncidentDetailScreen({ color={theme.colors.textInverse} /> ) : ( - + Resolve )} @@ -427,10 +345,8 @@ export default function IncidentDetailScreen({ {/* State Timeline */} {timeline && timeline.length > 0 ? ( - - + + State Timeline {timeline.map((entry: StateTimelineItem) => { @@ -440,32 +356,17 @@ export default function IncidentDetailScreen({ return ( - - + + {entry.incidentState?.name ?? "Unknown"} - + {formatDateTime(entry.createdAt)} @@ -476,31 +377,19 @@ export default function IncidentDetailScreen({ ) : null} {/* Internal Notes */} - - - + + + Internal Notes { return setNoteModalVisible(true); }} > - + Add Note @@ -511,39 +400,18 @@ export default function IncidentDetailScreen({ return ( - + {note.note} - + {note.createdByUser ? ( - + {note.createdByUser.name} ) : null} - + {formatDateTime(note.createdAt)} @@ -553,12 +421,7 @@ export default function IncidentDetailScreen({ : null} {notes && notes.length === 0 ? ( - + No notes yet. ) : null} @@ -575,130 +438,3 @@ export default function IncidentDetailScreen({ ); } - -const styles: ReturnType = StyleSheet.create({ - centered: { - flex: 1, - alignItems: "center", - justifyContent: "center", - }, - content: { - padding: 20, - paddingBottom: 40, - }, - numberBadge: { - alignSelf: "flex-start", - paddingHorizontal: 10, - paddingVertical: 4, - borderRadius: 8, - }, - number: { - fontSize: 14, - fontWeight: "600", - }, - badgeRow: { - flexDirection: "row", - flexWrap: "wrap", - gap: 8, - marginTop: 12, - }, - badge: { - flexDirection: "row", - alignItems: "center", - paddingHorizontal: 10, - paddingVertical: 5, - borderRadius: 6, - }, - dot: { - width: 8, - height: 8, - borderRadius: 4, - marginRight: 6, - }, - badgeText: { - fontSize: 13, - fontWeight: "600", - }, - section: { - marginTop: 24, - }, - sectionHeader: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 10, - }, - sectionTitle: { - fontSize: 13, - fontWeight: "600", - textTransform: "uppercase", - letterSpacing: 0.5, - marginBottom: 10, - }, - detailCard: { - borderRadius: 16, - padding: 16, - }, - detailRow: { - flexDirection: "row", - marginBottom: 10, - }, - detailLabel: { - fontSize: 14, - width: 90, - }, - detailValue: { - fontSize: 14, - }, - actionRow: { - flexDirection: "row", - gap: 12, - }, - actionButton: { - flex: 1, - paddingVertical: 14, - borderRadius: 14, - alignItems: "center", - justifyContent: "center", - minHeight: 50, - }, - actionButtonText: { - fontSize: 15, - fontWeight: "700", - }, - timelineEntry: { - flexDirection: "row", - alignItems: "center", - padding: 14, - borderRadius: 12, - marginBottom: 8, - }, - timelineDot: { - width: 10, - height: 10, - borderRadius: 5, - marginRight: 12, - }, - timelineInfo: { - flex: 1, - }, - addNoteButton: { - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 8, - }, - addNoteButtonText: { - fontSize: 13, - fontWeight: "600", - }, - noteCard: { - borderRadius: 12, - padding: 14, - marginBottom: 8, - }, - noteMeta: { - flexDirection: "row", - justifyContent: "space-between", - marginTop: 8, - }, -}); diff --git a/MobileApp/src/screens/IncidentEpisodeDetailScreen.tsx b/MobileApp/src/screens/IncidentEpisodeDetailScreen.tsx index b0f8b3ee93..afb1ea049e 100644 --- a/MobileApp/src/screens/IncidentEpisodeDetailScreen.tsx +++ b/MobileApp/src/screens/IncidentEpisodeDetailScreen.tsx @@ -7,7 +7,6 @@ import { ActivityIndicator, RefreshControl, Alert, - StyleSheet, } from "react-native"; import type { NativeStackScreenProps } from "@react-navigation/native-stack"; import { useTheme } from "../theme"; @@ -138,9 +137,7 @@ export default function IncidentEpisodeDetailScreen({ if (isLoading) { return ( - + ); @@ -148,18 +145,8 @@ export default function IncidentEpisodeDetailScreen({ if (!episode) { return ( - - + + Episode not found. @@ -191,46 +178,32 @@ export default function IncidentEpisodeDetailScreen({ return ( } > {/* Header */} - - + + {episode.episodeNumberWithPrefix || `#${episode.episodeNumber}`} - + {episode.title} {/* Badges */} - + {episode.currentIncidentState ? ( - - - + + + {episode.currentIncidentState.name} @@ -238,9 +211,13 @@ export default function IncidentEpisodeDetailScreen({ {episode.incidentSeverity ? ( - + {episode.incidentSeverity.name} @@ -249,83 +226,48 @@ export default function IncidentEpisodeDetailScreen({ {/* Description */} {episode.description ? ( - - + + Description - + {episode.description} ) : null} {/* Details */} - - + + Details - + {episode.declaredAt ? ( - - + + Declared - + {formatDateTime(episode.declaredAt)} ) : null} - - + + Created - + {formatDateTime(episode.createdAt)} - - + + Incidents - + {episode.incidentCount ?? 0} @@ -334,20 +276,15 @@ export default function IncidentEpisodeDetailScreen({ {/* State Change Actions */} {!isResolved ? ( - - + + Actions - + {!isAcknowledged && !isResolved && acknowledgeState ? ( { return handleStateChange( acknowledgeState._id, @@ -362,12 +299,7 @@ export default function IncidentEpisodeDetailScreen({ color={theme.colors.textInverse} /> ) : ( - + Acknowledge )} @@ -376,11 +308,8 @@ export default function IncidentEpisodeDetailScreen({ {resolveState ? ( { return handleStateChange(resolveState._id, resolveState.name); }} @@ -392,12 +321,7 @@ export default function IncidentEpisodeDetailScreen({ color={theme.colors.textInverse} /> ) : ( - + Resolve )} @@ -409,10 +333,8 @@ export default function IncidentEpisodeDetailScreen({ {/* State Timeline */} {timeline && timeline.length > 0 ? ( - - + + State Timeline {timeline.map((entry: StateTimelineItem) => { @@ -422,35 +344,17 @@ export default function IncidentEpisodeDetailScreen({ return ( - - + + {entry.incidentState?.name ?? "Unknown"} - + {formatDateTime(entry.createdAt)} @@ -461,31 +365,19 @@ export default function IncidentEpisodeDetailScreen({ ) : null} {/* Internal Notes */} - - - + + + Internal Notes { return setNoteModalVisible(true); }} > - + Add Note @@ -496,39 +388,18 @@ export default function IncidentEpisodeDetailScreen({ return ( - + {note.note} - + {note.createdByUser ? ( - + {note.createdByUser.name} ) : null} - + {formatDateTime(note.createdAt)} @@ -538,12 +409,7 @@ export default function IncidentEpisodeDetailScreen({ : null} {notes && notes.length === 0 ? ( - + No notes yet. ) : null} @@ -560,130 +426,3 @@ export default function IncidentEpisodeDetailScreen({ ); } - -const styles: ReturnType = StyleSheet.create({ - centered: { - flex: 1, - alignItems: "center", - justifyContent: "center", - }, - content: { - padding: 20, - paddingBottom: 40, - }, - numberBadge: { - alignSelf: "flex-start", - paddingHorizontal: 10, - paddingVertical: 4, - borderRadius: 8, - }, - number: { - fontSize: 14, - fontWeight: "600", - }, - badgeRow: { - flexDirection: "row", - flexWrap: "wrap", - gap: 8, - marginTop: 12, - }, - badge: { - flexDirection: "row", - alignItems: "center", - paddingHorizontal: 10, - paddingVertical: 5, - borderRadius: 6, - }, - dot: { - width: 8, - height: 8, - borderRadius: 4, - marginRight: 6, - }, - badgeText: { - fontSize: 13, - fontWeight: "600", - }, - section: { - marginTop: 24, - }, - sectionHeader: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 10, - }, - sectionTitle: { - fontSize: 13, - fontWeight: "600", - textTransform: "uppercase", - letterSpacing: 0.5, - marginBottom: 10, - }, - detailCard: { - borderRadius: 16, - padding: 16, - }, - detailRow: { - flexDirection: "row", - marginBottom: 10, - }, - detailLabel: { - fontSize: 14, - width: 90, - }, - detailValue: { - fontSize: 14, - }, - actionRow: { - flexDirection: "row", - gap: 12, - }, - actionButton: { - flex: 1, - paddingVertical: 14, - borderRadius: 14, - alignItems: "center", - justifyContent: "center", - minHeight: 50, - }, - actionButtonText: { - fontSize: 15, - fontWeight: "700", - }, - timelineEntry: { - flexDirection: "row", - alignItems: "center", - padding: 14, - borderRadius: 12, - marginBottom: 8, - }, - timelineDot: { - width: 10, - height: 10, - borderRadius: 5, - marginRight: 12, - }, - timelineInfo: { - flex: 1, - }, - addNoteButton: { - paddingHorizontal: 12, - paddingVertical: 6, - borderRadius: 8, - }, - addNoteButtonText: { - fontSize: 13, - fontWeight: "600", - }, - noteCard: { - borderRadius: 12, - padding: 14, - marginBottom: 8, - }, - noteMeta: { - flexDirection: "row", - justifyContent: "space-between", - marginTop: 8, - }, -}); diff --git a/MobileApp/src/screens/IncidentEpisodesScreen.tsx b/MobileApp/src/screens/IncidentEpisodesScreen.tsx index 2b131b20c0..7ed1010422 100644 --- a/MobileApp/src/screens/IncidentEpisodesScreen.tsx +++ b/MobileApp/src/screens/IncidentEpisodesScreen.tsx @@ -5,7 +5,6 @@ import { RefreshControl, TouchableOpacity, Text, - StyleSheet, ListRenderItemInfo, } from "react-native"; import { useNavigation } from "@react-navigation/native"; @@ -73,13 +72,8 @@ export default function IncidentEpisodesScreen(): React.JSX.Element { if (isLoading && episodes.length === 0) { return ( - - + + @@ -90,36 +84,18 @@ export default function IncidentEpisodesScreen(): React.JSX.Element { if (isError) { return ( - - + + Failed to load incident episodes. { return refetch(); }} > - + Retry @@ -128,19 +104,14 @@ export default function IncidentEpisodesScreen(): React.JSX.Element { } return ( - + { return item._id; }} contentContainerStyle={ - episodes.length === 0 ? styles.emptyContainer : styles.list + episodes.length === 0 ? { flex: 1 } : { padding: 16 } } renderItem={({ item }: ListRenderItemInfo) => { return ( @@ -169,30 +140,3 @@ export default function IncidentEpisodesScreen(): React.JSX.Element { ); } - -const styles: ReturnType = StyleSheet.create({ - container: { - flex: 1, - }, - centered: { - flex: 1, - alignItems: "center", - justifyContent: "center", - paddingHorizontal: 32, - }, - list: { - padding: 16, - }, - emptyContainer: { - flex: 1, - }, - skeletonList: { - padding: 16, - }, - retryButton: { - marginTop: 16, - paddingHorizontal: 24, - paddingVertical: 12, - borderRadius: 10, - }, -}); diff --git a/MobileApp/src/screens/IncidentsScreen.tsx b/MobileApp/src/screens/IncidentsScreen.tsx index 46fb8996ed..58124b3a5c 100644 --- a/MobileApp/src/screens/IncidentsScreen.tsx +++ b/MobileApp/src/screens/IncidentsScreen.tsx @@ -5,12 +5,11 @@ import { RefreshControl, TouchableOpacity, Text, - StyleSheet, ListRenderItemInfo, } from "react-native"; import { useNavigation } from "@react-navigation/native"; import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; -import { useTheme, type Theme } from "../theme"; +import { useTheme } from "../theme"; import { useProject } from "../hooks/useProject"; import { useIncidents } from "../hooks/useIncidents"; import { useIncidentStates } from "../hooks/useIncidentDetail"; @@ -32,7 +31,7 @@ type NavProp = NativeStackNavigationProp< >; export default function IncidentsScreen(): React.JSX.Element { - const { theme }: { theme: Theme } = useTheme(); + const { theme } = useTheme(); const { selectedProject } = useProject(); const projectId: string = selectedProject?._id ?? ""; const navigation: NativeStackNavigationProp< @@ -114,13 +113,8 @@ export default function IncidentsScreen(): React.JSX.Element { if (isLoading && incidents.length === 0) { return ( - - + + @@ -131,36 +125,18 @@ export default function IncidentsScreen(): React.JSX.Element { if (isError) { return ( - - + + Failed to load incidents. { return refetch(); }} > - + Retry @@ -169,19 +145,14 @@ export default function IncidentsScreen(): React.JSX.Element { } return ( - + { return item._id; }} contentContainerStyle={ - incidents.length === 0 ? styles.emptyContainer : styles.list + incidents.length === 0 ? { flex: 1 } : { padding: 16 } } renderItem={({ item }: ListRenderItemInfo) => { return ( @@ -224,30 +195,3 @@ export default function IncidentsScreen(): React.JSX.Element { ); } - -const styles: ReturnType = StyleSheet.create({ - container: { - flex: 1, - }, - centered: { - flex: 1, - alignItems: "center", - justifyContent: "center", - paddingHorizontal: 32, - }, - list: { - padding: 16, - }, - emptyContainer: { - flex: 1, - }, - skeletonList: { - padding: 16, - }, - retryButton: { - marginTop: 16, - paddingHorizontal: 24, - paddingVertical: 12, - borderRadius: 10, - }, -}); diff --git a/MobileApp/src/screens/NotificationPreferencesScreen.tsx b/MobileApp/src/screens/NotificationPreferencesScreen.tsx index e09c764810..e133066d06 100644 --- a/MobileApp/src/screens/NotificationPreferencesScreen.tsx +++ b/MobileApp/src/screens/NotificationPreferencesScreen.tsx @@ -1,13 +1,5 @@ import React, { useState, useEffect, useCallback } from "react"; -import { - View, - Text, - ScrollView, - Switch, - StyleSheet, - ViewStyle, - TextStyle, -} from "react-native"; +import { View, Text, ScrollView, Switch } from "react-native"; import { useTheme } from "../theme"; import { useHaptics } from "../hooks/useHaptics"; import { @@ -33,24 +25,16 @@ function PrefRow({ return ( - - + + {label} - + {description} @@ -102,33 +86,27 @@ export default function NotificationPreferencesScreen(): React.JSX.Element { if (!loaded) { return ( ); } return ( {/* Event Types */} - - + + Event Types - + Choose which event types send push notifications - + {/* Priority Filter */} - - + + Priority - + {/* Info */} - - + + Notification preferences are stored locally on this device. Server-side notification rules configured in your project settings take precedence. @@ -195,75 +171,3 @@ export default function NotificationPreferencesScreen(): React.JSX.Element { ); } - -const styles: { - container: ViewStyle; - content: ViewStyle; - section: ViewStyle; - sectionTitle: TextStyle; - sectionHint: TextStyle; - rowGroup: ViewStyle; - row: ViewStyle; - rowText: ViewStyle; - rowLabel: TextStyle; - rowDescription: TextStyle; - infoSection: ViewStyle; - infoText: TextStyle; -} = StyleSheet.create({ - container: { - flex: 1, - }, - content: { - padding: 20, - paddingBottom: 60, - }, - section: { - marginBottom: 28, - }, - sectionTitle: { - fontSize: 13, - fontWeight: "600", - textTransform: "uppercase", - letterSpacing: 0.8, - marginBottom: 4, - marginLeft: 4, - }, - sectionHint: { - fontSize: 12, - marginBottom: 12, - marginLeft: 4, - lineHeight: 16, - }, - rowGroup: { - gap: 1, - }, - row: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - padding: 16, - borderRadius: 16, - marginBottom: 10, - }, - rowText: { - flex: 1, - marginRight: 12, - }, - rowLabel: { - fontSize: 16, - fontWeight: "500", - }, - rowDescription: { - fontSize: 13, - marginTop: 2, - lineHeight: 18, - }, - infoSection: { - marginTop: 4, - paddingHorizontal: 4, - }, - infoText: { - fontSize: 12, - lineHeight: 18, - }, -}); diff --git a/MobileApp/src/screens/ProjectSelectionScreen.tsx b/MobileApp/src/screens/ProjectSelectionScreen.tsx index f25331546f..8dac8bb3ac 100644 --- a/MobileApp/src/screens/ProjectSelectionScreen.tsx +++ b/MobileApp/src/screens/ProjectSelectionScreen.tsx @@ -5,7 +5,6 @@ import { FlatList, TouchableOpacity, ActivityIndicator, - StyleSheet, ListRenderItemInfo, } from "react-native"; import { useTheme } from "../theme"; @@ -25,19 +24,9 @@ export default function ProjectSelectionScreen(): React.JSX.Element { if (isLoadingProjects) { return ( - + - + Loading projects... @@ -46,45 +35,19 @@ export default function ProjectSelectionScreen(): React.JSX.Element { if (projectList.length === 0) { return ( - - + + No Projects Found - + {"You don't have access to any projects."} - + Retry @@ -93,27 +56,12 @@ export default function ProjectSelectionScreen(): React.JSX.Element { } return ( - - - + + + Select Project - + Choose a project to view incidents and alerts. @@ -123,44 +71,26 @@ export default function ProjectSelectionScreen(): React.JSX.Element { keyExtractor={(item: ProjectItem) => { return item._id; }} - contentContainerStyle={styles.list} + contentContainerStyle={{ padding: 20, paddingTop: 12 }} renderItem={({ item }: ListRenderItemInfo) => { return ( { return handleSelect(item); }} activeOpacity={0.7} > - - + + {item.name} {item.slug ? ( - + {item.slug} ) : null} @@ -172,46 +102,3 @@ export default function ProjectSelectionScreen(): React.JSX.Element { ); } - -const styles: ReturnType = StyleSheet.create({ - container: { - flex: 1, - }, - centered: { - flex: 1, - alignItems: "center", - justifyContent: "center", - paddingHorizontal: 32, - }, - header: { - paddingHorizontal: 20, - paddingTop: 16, - paddingBottom: 8, - }, - list: { - padding: 20, - paddingTop: 12, - }, - projectCard: { - flexDirection: "row", - alignItems: "center", - padding: 18, - borderRadius: 16, - marginBottom: 12, - }, - projectDot: { - width: 14, - height: 14, - borderRadius: 7, - marginRight: 12, - }, - projectInfo: { - flex: 1, - }, - retryButton: { - marginTop: 24, - paddingHorizontal: 32, - paddingVertical: 14, - borderRadius: 12, - }, -}); diff --git a/MobileApp/src/screens/SettingsScreen.tsx b/MobileApp/src/screens/SettingsScreen.tsx index 6b554891bd..65b8535c7b 100644 --- a/MobileApp/src/screens/SettingsScreen.tsx +++ b/MobileApp/src/screens/SettingsScreen.tsx @@ -5,7 +5,6 @@ import { TouchableOpacity, ScrollView, Switch, - StyleSheet, } from "react-native"; import { useTheme, ThemeMode } from "../theme"; import { useNavigation } from "@react-navigation/native"; @@ -44,38 +43,22 @@ function SettingsRow({ const { theme } = useTheme(); const content: React.JSX.Element = ( - + {label} {rightElement ?? (value ? ( - - {value} - + {value} ) : onPress ? ( - - › - + {">"} ) : null)} ); @@ -126,61 +109,47 @@ export default function SettingsScreen(): React.JSX.Element { return ( {/* Appearance */} - - + + Appearance - + {(["dark", "light", "system"] as ThemeMode[]).map( (mode: ThemeMode) => { const isActive: boolean = themeMode === mode; return ( { return handleThemeChange(mode); }} activeOpacity={0.7} > - {mode === "dark" ? "◗" : mode === "light" ? "○" : "◑"} + {mode === "dark" ? "\u25D7" : mode === "light" ? "\u25CB" : "\u25D1"} {mode.charAt(0).toUpperCase() + mode.slice(1)} @@ -192,10 +161,8 @@ export default function SettingsScreen(): React.JSX.Element { {/* Notifications */} - - + + Notifications - + + Security } /> - + Require {biometric.biometricType.toLowerCase()} to unlock the app @@ -238,10 +201,8 @@ export default function SettingsScreen(): React.JSX.Element { {/* Project */} {selectedProject ? ( - - + + Project - + + Server {/* Account */} - - + + Account {/* About */} - - + + About - + {/* Footer branding */} - - + + OneUptime On-Call ); } - -const styles: ReturnType = StyleSheet.create({ - content: { - padding: 20, - paddingBottom: 60, - }, - section: { - marginBottom: 28, - }, - sectionTitle: { - fontSize: 13, - fontWeight: "600", - textTransform: "uppercase", - letterSpacing: 0.8, - marginBottom: 10, - marginLeft: 4, - }, - sectionHint: { - fontSize: 12, - marginTop: 8, - marginLeft: 4, - lineHeight: 16, - }, - row: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - padding: 16, - borderRadius: 16, - minHeight: 52, - }, - rowLabel: { - fontSize: 16, - fontWeight: "500", - }, - rowValue: { - fontSize: 15, - }, - chevron: { - fontSize: 24, - fontWeight: "300", - }, - // Theme selector - themeSelector: { - flexDirection: "row", - borderRadius: 16, - padding: 4, - gap: 4, - }, - themeOption: { - flex: 1, - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - paddingVertical: 10, - borderRadius: 8, - gap: 6, - }, - themeOptionIcon: { - fontSize: 16, - }, - themeOptionLabel: { - fontSize: 14, - fontWeight: "600", - }, - // Footer - footer: { - alignItems: "center", - paddingTop: 12, - }, - footerText: { - fontSize: 12, - fontWeight: "500", - }, -}); diff --git a/MobileApp/src/screens/auth/LoginScreen.tsx b/MobileApp/src/screens/auth/LoginScreen.tsx index dfabbb6852..8211f3a8fb 100644 --- a/MobileApp/src/screens/auth/LoginScreen.tsx +++ b/MobileApp/src/screens/auth/LoginScreen.tsx @@ -4,13 +4,10 @@ import { Text, TextInput, TouchableOpacity, - StyleSheet, ActivityIndicator, KeyboardAvoidingView, Platform, ScrollView, - ViewStyle, - TextStyle, } from "react-native"; import { useTheme } from "../../theme"; import { useAuth } from "../../hooks/useAuth"; @@ -74,62 +71,29 @@ export default function LoginScreen(): React.JSX.Element { return ( - - - + + + OneUptime - + {serverUrl} - - + + Email { setEmail(text); @@ -144,27 +108,11 @@ export default function LoginScreen(): React.JSX.Element { returnKeyType="next" /> - + Password { setPassword(text); @@ -179,55 +127,34 @@ export default function LoginScreen(): React.JSX.Element { /> {error ? ( - + {error} ) : null} {isLoading ? ( ) : ( - + Log In )} - + Change Server @@ -237,52 +164,3 @@ export default function LoginScreen(): React.JSX.Element { ); } - -const styles: { - flex: ViewStyle; - scrollContent: ViewStyle; - container: ViewStyle; - header: ViewStyle; - form: ViewStyle; - input: TextStyle; - button: ViewStyle; - changeServer: ViewStyle; -} = StyleSheet.create({ - flex: { - flex: 1, - }, - scrollContent: { - flexGrow: 1, - }, - container: { - flex: 1, - justifyContent: "center", - paddingHorizontal: 24, - }, - header: { - alignItems: "center", - marginBottom: 48, - }, - form: { - width: "100%", - }, - input: { - height: 56, - borderWidth: 1, - borderRadius: 14, - paddingHorizontal: 16, - fontSize: 16, - }, - button: { - height: 52, - borderRadius: 14, - alignItems: "center", - justifyContent: "center", - marginTop: 24, - }, - changeServer: { - alignItems: "center", - marginTop: 24, - paddingVertical: 8, - }, -}); diff --git a/MobileApp/src/screens/auth/ServerUrlScreen.tsx b/MobileApp/src/screens/auth/ServerUrlScreen.tsx index 714647452a..a74eb35b27 100644 --- a/MobileApp/src/screens/auth/ServerUrlScreen.tsx +++ b/MobileApp/src/screens/auth/ServerUrlScreen.tsx @@ -4,13 +4,10 @@ import { Text, TextInput, TouchableOpacity, - StyleSheet, ActivityIndicator, KeyboardAvoidingView, Platform, ScrollView, - ViewStyle, - TextStyle, } from "react-native"; import { NativeStackNavigationProp } from "@react-navigation/native-stack"; import { useNavigation } from "@react-navigation/native"; @@ -66,65 +63,34 @@ export default function ServerUrlScreen(): React.JSX.Element { return ( - - - + + + OneUptime - + Connect to your OneUptime instance - - + + Server URL { setUrl(text); @@ -140,55 +106,30 @@ export default function ServerUrlScreen(): React.JSX.Element { /> {error ? ( - + {error} ) : null} {isLoading ? ( ) : ( - + Connect )} - + Self-hosting? Enter your OneUptime server URL above. @@ -197,46 +138,3 @@ export default function ServerUrlScreen(): React.JSX.Element { ); } - -const styles: { - flex: ViewStyle; - scrollContent: ViewStyle; - container: ViewStyle; - header: ViewStyle; - form: ViewStyle; - input: TextStyle; - button: ViewStyle; -} = StyleSheet.create({ - flex: { - flex: 1, - }, - scrollContent: { - flexGrow: 1, - }, - container: { - flex: 1, - justifyContent: "center", - paddingHorizontal: 24, - }, - header: { - alignItems: "center", - marginBottom: 48, - }, - form: { - width: "100%", - }, - input: { - height: 56, - borderWidth: 1, - borderRadius: 14, - paddingHorizontal: 16, - fontSize: 16, - }, - button: { - height: 52, - borderRadius: 14, - alignItems: "center", - justifyContent: "center", - marginTop: 24, - }, -}); diff --git a/MobileApp/src/theme/ThemeContext.tsx b/MobileApp/src/theme/ThemeContext.tsx index b5f36589f9..57b95c6c6a 100644 --- a/MobileApp/src/theme/ThemeContext.tsx +++ b/MobileApp/src/theme/ThemeContext.tsx @@ -6,11 +6,8 @@ import React, { useMemo, ReactNode, } from "react"; -import { useColorScheme } from "react-native"; +import { View, useColorScheme } from "react-native"; import { ColorTokens, darkColors, lightColors } from "./colors"; -import { typography } from "./typography"; -import { spacing, radius } from "./spacing"; -import { shadows, ShadowTokens } from "./shadows"; import { getThemeMode as loadThemeMode, setThemeMode as saveThemeMode, @@ -20,10 +17,6 @@ export type ThemeMode = "dark" | "light" | "system"; export interface Theme { colors: ColorTokens; - typography: typeof typography; - spacing: typeof spacing; - radius: typeof radius; - shadows: ShadowTokens; isDark: boolean; } @@ -70,10 +63,6 @@ export function ThemeProvider({ return { colors: isDark ? darkColors : lightColors, - typography, - spacing, - radius, - shadows, isDark, }; }, [themeMode, systemColorScheme]); @@ -87,7 +76,11 @@ export function ThemeProvider({ }, [theme, themeMode]); return ( - {children} + + + {children} + + ); } diff --git a/MobileApp/src/theme/index.ts b/MobileApp/src/theme/index.ts index bb734a82c9..1604d23a9d 100644 --- a/MobileApp/src/theme/index.ts +++ b/MobileApp/src/theme/index.ts @@ -1,8 +1,4 @@ export { darkColors, lightColors } from "./colors"; export type { ColorTokens } from "./colors"; -export { typography } from "./typography"; -export { spacing, radius } from "./spacing"; -export { shadows } from "./shadows"; -export type { ShadowTokens } from "./shadows"; export { ThemeProvider, useTheme } from "./ThemeContext"; export type { Theme, ThemeMode } from "./ThemeContext"; diff --git a/MobileApp/src/theme/shadows.ts b/MobileApp/src/theme/shadows.ts deleted file mode 100644 index 41db6a6f73..0000000000 --- a/MobileApp/src/theme/shadows.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ViewStyle } from "react-native"; - -export interface ShadowTokens { - sm: ViewStyle; - md: ViewStyle; - lg: ViewStyle; -} - -export const shadows: ShadowTokens = { - sm: { - shadowColor: "#000", - shadowOffset: { width: 0, height: 1 }, - shadowOpacity: 0.04, - shadowRadius: 6, - elevation: 1, - }, - md: { - shadowColor: "#000", - shadowOffset: { width: 0, height: 2 }, - shadowOpacity: 0.06, - shadowRadius: 12, - elevation: 3, - }, - lg: { - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.08, - shadowRadius: 16, - elevation: 5, - }, -}; diff --git a/MobileApp/src/theme/spacing.ts b/MobileApp/src/theme/spacing.ts deleted file mode 100644 index 1bbaf29613..0000000000 --- a/MobileApp/src/theme/spacing.ts +++ /dev/null @@ -1,28 +0,0 @@ -export const spacing: { - readonly xs: 4; - readonly sm: 8; - readonly md: 16; - readonly lg: 24; - readonly xl: 32; - readonly xxl: 48; -} = { - xs: 4, - sm: 8, - md: 16, - lg: 24, - xl: 32, - xxl: 48, -} as const; - -export const radius: { - readonly sm: 6; - readonly md: 12; - readonly lg: 16; -} = { - sm: 6, - md: 12, - lg: 16, -} as const; - -export type Spacing = typeof spacing; -export type Radius = typeof radius; diff --git a/MobileApp/src/theme/typography.ts b/MobileApp/src/theme/typography.ts deleted file mode 100644 index d525d3be70..0000000000 --- a/MobileApp/src/theme/typography.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Platform, TextStyle } from "react-native"; - -const fontFamily: string = Platform.OS === "ios" ? "System" : "Roboto"; -const monoFontFamily: string = Platform.OS === "ios" ? "Menlo" : "monospace"; - -interface TypographyStyles { - titleLarge: TextStyle; - titleMedium: TextStyle; - titleSmall: TextStyle; - bodyLarge: TextStyle; - bodyMedium: TextStyle; - bodySmall: TextStyle; - monoLarge: TextStyle; - monoMedium: TextStyle; - monoSmall: TextStyle; - label: TextStyle; - caption: TextStyle; -} - -export const typography: TypographyStyles = { - titleLarge: { - fontFamily, - fontSize: 28, - fontWeight: "700", - lineHeight: 34, - }, - titleMedium: { - fontFamily, - fontSize: 22, - fontWeight: "600", - lineHeight: 28, - }, - titleSmall: { - fontFamily, - fontSize: 17, - fontWeight: "600", - lineHeight: 22, - }, - bodyLarge: { - fontFamily, - fontSize: 17, - fontWeight: "400", - lineHeight: 24, - }, - bodyMedium: { - fontFamily, - fontSize: 15, - fontWeight: "400", - lineHeight: 20, - }, - bodySmall: { - fontFamily, - fontSize: 13, - fontWeight: "400", - lineHeight: 18, - }, - monoLarge: { - fontFamily: monoFontFamily, - fontSize: 17, - fontWeight: "400", - lineHeight: 24, - }, - monoMedium: { - fontFamily: monoFontFamily, - fontSize: 15, - fontWeight: "400", - lineHeight: 20, - }, - monoSmall: { - fontFamily: monoFontFamily, - fontSize: 13, - fontWeight: "400", - lineHeight: 18, - }, - label: { - fontFamily, - fontSize: 12, - fontWeight: "600", - lineHeight: 16, - letterSpacing: 0.5, - textTransform: "uppercase", - }, - caption: { - fontFamily, - fontSize: 12, - fontWeight: "400", - lineHeight: 16, - }, -}; - -export default typography; diff --git a/MobileApp/tailwind.config.ts b/MobileApp/tailwind.config.ts new file mode 100644 index 0000000000..69025b6bf5 --- /dev/null +++ b/MobileApp/tailwind.config.ts @@ -0,0 +1,80 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + presets: [require("nativewind/preset")], + darkMode: "class", + content: ["./App.tsx", "./src/**/*.{ts,tsx}"], + theme: { + extend: { + colors: { + "bg-primary": "var(--color-bg-primary)", + "bg-secondary": "var(--color-bg-secondary)", + "bg-tertiary": "var(--color-bg-tertiary)", + "bg-elevated": "var(--color-bg-elevated)", + "border-default": "var(--color-border-default)", + "border-subtle": "var(--color-border-subtle)", + "text-primary": "var(--color-text-primary)", + "text-secondary": "var(--color-text-secondary)", + "text-tertiary": "var(--color-text-tertiary)", + "text-inverse": "var(--color-text-inverse)", + "severity-critical": "var(--color-severity-critical)", + "severity-critical-bg": "var(--color-severity-critical-bg)", + "severity-major": "var(--color-severity-major)", + "severity-major-bg": "var(--color-severity-major-bg)", + "severity-minor": "var(--color-severity-minor)", + "severity-minor-bg": "var(--color-severity-minor-bg)", + "severity-warning": "var(--color-severity-warning)", + "severity-warning-bg": "var(--color-severity-warning-bg)", + "severity-info": "var(--color-severity-info)", + "severity-info-bg": "var(--color-severity-info-bg)", + "state-created": "var(--color-state-created)", + "state-acknowledged": "var(--color-state-acknowledged)", + "state-resolved": "var(--color-state-resolved)", + "state-investigating": "var(--color-state-investigating)", + "state-muted": "var(--color-state-muted)", + "oncall-active": "var(--color-oncall-active)", + "oncall-active-bg": "var(--color-oncall-active-bg)", + "oncall-inactive": "var(--color-oncall-inactive)", + "oncall-inactive-bg": "var(--color-oncall-inactive-bg)", + "action-primary": "var(--color-action-primary)", + "action-primary-pressed": "var(--color-action-primary-pressed)", + "action-destructive": "var(--color-action-destructive)", + "action-destructive-pressed": "var(--color-action-destructive-pressed)", + "status-success": "var(--color-status-success)", + "status-success-bg": "var(--color-status-success-bg)", + "status-error": "var(--color-status-error)", + "status-error-bg": "var(--color-status-error-bg)", + }, + fontSize: { + "title-lg": [ + "28px", + { lineHeight: "34px", fontWeight: "700" }, + ], + "title-md": [ + "22px", + { lineHeight: "28px", fontWeight: "600" }, + ], + "title-sm": [ + "17px", + { lineHeight: "22px", fontWeight: "600" }, + ], + "body-lg": ["17px", { lineHeight: "24px" }], + "body-md": ["15px", { lineHeight: "20px" }], + "body-sm": ["13px", { lineHeight: "18px" }], + label: [ + "12px", + { lineHeight: "16px", fontWeight: "600", letterSpacing: "0.5px" }, + ], + caption: ["12px", { lineHeight: "16px" }], + }, + boxShadow: { + sm: "0 1px 6px rgba(0,0,0,0.04)", + md: "0 2px 12px rgba(0,0,0,0.06)", + lg: "0 4px 16px rgba(0,0,0,0.08)", + }, + }, + }, + plugins: [], +}; + +export default config; diff --git a/MobileApp/tsconfig.json b/MobileApp/tsconfig.json index 6b574f4ca3..beaf8d8a97 100644 --- a/MobileApp/tsconfig.json +++ b/MobileApp/tsconfig.json @@ -5,6 +5,7 @@ "baseUrl": ".", "paths": { "@/*": ["src/*"] - } + }, + "types": ["nativewind/types"] } }