openuispec 0.2.10 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/cli/index.ts +4 -2
- package/cli/init.ts +23 -8
- package/docs/cli.md +13 -8
- package/drift/index.ts +41 -15
- package/mcp-server/index.ts +155 -116
- package/mcp-server/screenshot.ts +19 -4
- package/package.json +5 -2
- package/prepare/index.ts +16 -0
- package/scripts/take-all-screenshots.ts +507 -0
- package/status/index.ts +2 -2
- package/examples/social-app/.mcp.json +0 -10
- package/examples/social-app/AGENTS.md +0 -124
- package/examples/social-app/CLAUDE.md +0 -124
- package/examples/social-app/backend/.gitkeep +0 -1
- package/examples/social-app/generated/android/social-app/app/.paparazzi-hashes.json +0 -3
- package/examples/social-app/generated/android/social-app/app/build.gradle.kts +0 -94
- package/examples/social-app/generated/android/social-app/app/src/main/AndroidManifest.xml +0 -26
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/AppContainer.kt +0 -20
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/MainActivity.kt +0 -35
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/SocialAppApplication.kt +0 -13
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/MockData.kt +0 -98
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/AppPreferences.kt +0 -19
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/DataStorePreferencesRepository.kt +0 -68
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/data/preferences/PreferencesRepository.kt +0 -15
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/model/Models.kt +0 -34
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/MainShell.kt +0 -390
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/Components.kt +0 -234
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/components/ContractPrimitives.kt +0 -641
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/navigation/RootComponent.kt +0 -113
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ChatDetailScreen.kt +0 -212
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/CreatePostScreen.kt +0 -113
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/DiscoverScreen.kt +0 -137
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/EditProfileScreen.kt +0 -180
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/HomeFeedScreen.kt +0 -169
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/MessagesInboxScreen.kt +0 -85
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/NotificationsScreen.kt +0 -74
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/PostDetailScreen.kt +0 -293
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/ProfileSelfScreen.kt +0 -116
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SearchResultsScreen.kt +0 -161
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsScreen.kt +0 -164
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/SettingsStore.kt +0 -95
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/screens/UserProfileScreen.kt +0 -123
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Color.kt +0 -33
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Shape.kt +0 -41
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Spacing.kt +0 -20
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Theme.kt +0 -82
- package/examples/social-app/generated/android/social-app/app/src/main/java/com/social/app/ui/theme/Type.kt +0 -60
- package/examples/social-app/generated/android/social-app/app/src/main/res/drawable/ic_launcher_foreground.xml +0 -9
- package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +0 -5
- package/examples/social-app/generated/android/social-app/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +0 -5
- package/examples/social-app/generated/android/social-app/app/src/main/res/values/strings.xml +0 -91
- package/examples/social-app/generated/android/social-app/app/src/main/res/values/themes.xml +0 -10
- package/examples/social-app/generated/android/social-app/app/src/main/res/values-ru/strings.xml +0 -79
- package/examples/social-app/generated/android/social-app/app/src/main/res/values-uz/strings.xml +0 -79
- package/examples/social-app/generated/android/social-app/app/src/main/xml/AndroidManifest.xml +0 -23
- package/examples/social-app/generated/android/social-app/app/src/test/kotlin/com/social/app/screenshots/HomeFeedScreenshotTest.kt +0 -34
- package/examples/social-app/generated/android/social-app/build.gradle.kts +0 -7
- package/examples/social-app/generated/android/social-app/gradle/libs.versions.toml +0 -50
- package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/social-app/generated/android/social-app/gradle/wrapper/gradle-wrapper.properties +0 -8
- package/examples/social-app/generated/android/social-app/gradle.properties +0 -11
- package/examples/social-app/generated/android/social-app/gradlew +0 -248
- package/examples/social-app/generated/android/social-app/settings.gradle.kts +0 -27
- package/examples/social-app/generated/web/social-app/index.html +0 -12
- package/examples/social-app/generated/web/social-app/package-lock.json +0 -2517
- package/examples/social-app/generated/web/social-app/package.json +0 -27
- package/examples/social-app/generated/web/social-app/src/app/App.tsx +0 -58
- package/examples/social-app/generated/web/social-app/src/components/Shell.tsx +0 -259
- package/examples/social-app/generated/web/social-app/src/components/cards.tsx +0 -317
- package/examples/social-app/generated/web/social-app/src/components/ui.tsx +0 -340
- package/examples/social-app/generated/web/social-app/src/flows/CreatePostFlow.tsx +0 -86
- package/examples/social-app/generated/web/social-app/src/i18n.tsx +0 -59
- package/examples/social-app/generated/web/social-app/src/lib/icons.tsx +0 -85
- package/examples/social-app/generated/web/social-app/src/lib/tokens.ts +0 -70
- package/examples/social-app/generated/web/social-app/src/lib/utils.ts +0 -97
- package/examples/social-app/generated/web/social-app/src/locales/en.json +0 -67
- package/examples/social-app/generated/web/social-app/src/locales/ru.json +0 -67
- package/examples/social-app/generated/web/social-app/src/locales/uz.json +0 -67
- package/examples/social-app/generated/web/social-app/src/main.tsx +0 -16
- package/examples/social-app/generated/web/social-app/src/screens/ChatDetailScreen.tsx +0 -90
- package/examples/social-app/generated/web/social-app/src/screens/DiscoverScreen.tsx +0 -86
- package/examples/social-app/generated/web/social-app/src/screens/EditProfileScreen.tsx +0 -57
- package/examples/social-app/generated/web/social-app/src/screens/HomeFeedScreen.tsx +0 -103
- package/examples/social-app/generated/web/social-app/src/screens/MessagesInboxScreen.tsx +0 -52
- package/examples/social-app/generated/web/social-app/src/screens/NotificationsScreen.tsx +0 -41
- package/examples/social-app/generated/web/social-app/src/screens/PostDetailScreen.tsx +0 -115
- package/examples/social-app/generated/web/social-app/src/screens/ProfileSelfScreen.tsx +0 -57
- package/examples/social-app/generated/web/social-app/src/screens/ProfileUserScreen.tsx +0 -76
- package/examples/social-app/generated/web/social-app/src/screens/SearchResultsScreen.tsx +0 -96
- package/examples/social-app/generated/web/social-app/src/screens/SettingsScreen.tsx +0 -79
- package/examples/social-app/generated/web/social-app/src/state/store.ts +0 -592
- package/examples/social-app/generated/web/social-app/src/styles.css +0 -125
- package/examples/social-app/generated/web/social-app/src/vite-env.d.ts +0 -1
- package/examples/social-app/generated/web/social-app/tsconfig.json +0 -22
- package/examples/social-app/generated/web/social-app/tsconfig.node.json +0 -13
- package/examples/social-app/generated/web/social-app/tsconfig.node.tsbuildinfo +0 -1
- package/examples/social-app/generated/web/social-app/tsconfig.tsbuildinfo +0 -1
- package/examples/social-app/generated/web/social-app/vite.config.d.ts +0 -2
- package/examples/social-app/generated/web/social-app/vite.config.js +0 -6
- package/examples/social-app/generated/web/social-app/vite.config.ts +0 -7
- package/examples/social-app/package.json +0 -13
- package/examples/social-app/take-web-screenshots.ts +0 -97
- package/examples/taskflow/.codex/config.toml +0 -4
- package/examples/taskflow/.mcp.json +0 -10
- package/examples/taskflow/AGENTS.md +0 -124
- package/examples/taskflow/CLAUDE.md +0 -124
- package/examples/taskflow/backend/.gitkeep +0 -1
- package/examples/taskflow/generated/android/TaskFlow/README.md +0 -43
- package/examples/taskflow/generated/android/TaskFlow/app/build.gradle.kts +0 -76
- package/examples/taskflow/generated/android/TaskFlow/app/proguard-rules.pro +0 -1
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/AndroidManifest.xml +0 -21
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/MainActivity.kt +0 -19
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/TaskFlowApp.kt +0 -283
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/DomainModels.kt +0 -106
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/model/SampleData.kt +0 -57
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/components/Common.kt +0 -109
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/HomeScreen.kt +0 -112
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/ProjectsScreen.kt +0 -61
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/SettingsScreen.kt +0 -82
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/screens/TaskDetailScreen.kt +0 -111
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/sheets/Sheets.kt +0 -77
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Color.kt +0 -30
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Theme.kt +0 -86
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/java/uz/rsteam/taskflow/ui/theme/Type.kt +0 -57
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/strings.xml +0 -155
- package/examples/taskflow/generated/android/TaskFlow/app/src/main/res/values/themes.xml +0 -4
- package/examples/taskflow/generated/android/TaskFlow/build.gradle.kts +0 -5
- package/examples/taskflow/generated/android/TaskFlow/gradle/gradle-daemon-jvm.properties +0 -12
- package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/taskflow/generated/android/TaskFlow/gradle/wrapper/gradle-wrapper.properties +0 -7
- package/examples/taskflow/generated/android/TaskFlow/gradle.properties +0 -4
- package/examples/taskflow/generated/android/TaskFlow/gradlew +0 -18
- package/examples/taskflow/generated/android/TaskFlow/gradlew.bat +0 -12
- package/examples/taskflow/generated/android/TaskFlow/settings.gradle.kts +0 -18
- package/examples/taskflow/generated/ios/TaskFlow/README.md +0 -21
- package/examples/taskflow/generated/ios/TaskFlow/Resources/en.lproj/Localizable.strings +0 -115
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/App/TaskFlowApp.swift +0 -24
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Components/AppChrome.swift +0 -150
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Flows/TaskEditorSheet.swift +0 -220
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Models/DomainModels.swift +0 -122
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/CalendarView.swift +0 -21
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/HomeView.swift +0 -201
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProfileEditView.swift +0 -48
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectDetailView.swift +0 -59
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/ProjectsView.swift +0 -63
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/SettingsView.swift +0 -85
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Screens/TaskDetailView.swift +0 -219
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppModel.swift +0 -320
- package/examples/taskflow/generated/ios/TaskFlow/Sources/TaskFlow/Support/AppSupport.swift +0 -41
- package/examples/taskflow/generated/ios/TaskFlow/project.yml +0 -31
- package/examples/taskflow/generated/web/TaskFlow/README.md +0 -19
- package/examples/taskflow/generated/web/TaskFlow/index.html +0 -12
- package/examples/taskflow/generated/web/TaskFlow/package-lock.json +0 -1908
- package/examples/taskflow/generated/web/TaskFlow/package.json +0 -24
- package/examples/taskflow/generated/web/TaskFlow/src/App.tsx +0 -58
- package/examples/taskflow/generated/web/TaskFlow/src/AppShell.tsx +0 -55
- package/examples/taskflow/generated/web/TaskFlow/src/components/Common.tsx +0 -82
- package/examples/taskflow/generated/web/TaskFlow/src/components/Modals.tsx +0 -191
- package/examples/taskflow/generated/web/TaskFlow/src/components/Nav.tsx +0 -41
- package/examples/taskflow/generated/web/TaskFlow/src/generated/messages.ts +0 -131
- package/examples/taskflow/generated/web/TaskFlow/src/hooks.ts +0 -25
- package/examples/taskflow/generated/web/TaskFlow/src/i18n.ts +0 -39
- package/examples/taskflow/generated/web/TaskFlow/src/locales.en.json +0 -111
- package/examples/taskflow/generated/web/TaskFlow/src/main.tsx +0 -13
- package/examples/taskflow/generated/web/TaskFlow/src/screens/HomeScreen.tsx +0 -111
- package/examples/taskflow/generated/web/TaskFlow/src/screens/ProjectsScreen.tsx +0 -82
- package/examples/taskflow/generated/web/TaskFlow/src/screens/SettingsScreens.tsx +0 -132
- package/examples/taskflow/generated/web/TaskFlow/src/screens/TaskDetail.tsx +0 -105
- package/examples/taskflow/generated/web/TaskFlow/src/store.ts +0 -216
- package/examples/taskflow/generated/web/TaskFlow/src/styles.css +0 -617
- package/examples/taskflow/generated/web/TaskFlow/src/types.ts +0 -64
- package/examples/taskflow/generated/web/TaskFlow/src/utils.ts +0 -78
- package/examples/taskflow/generated/web/TaskFlow/tsconfig.json +0 -21
- package/examples/taskflow/generated/web/TaskFlow/vite.config.ts +0 -6
- package/examples/todo-orbit/.codex/config.toml +0 -4
- package/examples/todo-orbit/.mcp.json +0 -10
- package/examples/todo-orbit/AGENTS.md +0 -124
- package/examples/todo-orbit/CLAUDE.md +0 -124
- package/examples/todo-orbit/backend/.gitkeep +0 -1
- package/examples/todo-orbit/generated/android/Todo Orbit/README.md +0 -14
- package/examples/todo-orbit/generated/android/Todo Orbit/app/build.gradle.kts +0 -58
- package/examples/todo-orbit/generated/android/Todo Orbit/app/proguard-rules.pro +0 -1
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/AndroidManifest.xml +0 -20
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/MainActivity.kt +0 -14
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/TodoOrbitApp.kt +0 -345
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/AppLogic.kt +0 -231
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Models.kt +0 -169
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/support/Strings.kt +0 -8
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/components/CommonComponents.kt +0 -236
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/AnalyticsScreen.kt +0 -193
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/SettingsScreen.kt +0 -102
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/screens/TasksScreen.kt +0 -347
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/sheets/EditorSheets.kt +0 -347
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/java/uz/rsteam/todoorbit/ui/theme/TodoOrbitTheme.kt +0 -59
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values/strings.xml +0 -149
- package/examples/todo-orbit/generated/android/Todo Orbit/app/src/main/res/values-ru/strings.xml +0 -155
- package/examples/todo-orbit/generated/android/Todo Orbit/build.gradle.kts +0 -4
- package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/todo-orbit/generated/android/Todo Orbit/gradle/wrapper/gradle-wrapper.properties +0 -7
- package/examples/todo-orbit/generated/android/Todo Orbit/gradle.properties +0 -4
- package/examples/todo-orbit/generated/android/Todo Orbit/gradlew +0 -248
- package/examples/todo-orbit/generated/android/Todo Orbit/gradlew.bat +0 -93
- package/examples/todo-orbit/generated/android/Todo Orbit/settings.gradle.kts +0 -18
- package/examples/todo-orbit/generated/ios/Todo Orbit/.screenshot-uitest/Sources/ScreenshotUITest.swift +0 -36
- package/examples/todo-orbit/generated/ios/Todo Orbit/README.md +0 -29
- package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/en.lproj/Localizable.strings +0 -119
- package/examples/todo-orbit/generated/ios/Todo Orbit/Resources/ru.lproj/Localizable.strings +0 -119
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/App/TodoOrbitApp.swift +0 -50
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/OrbitChrome.swift +0 -204
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/SchedulePreviewView.swift +0 -126
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Components/TrendChartView.swift +0 -70
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/RecurringRuleSheet.swift +0 -126
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Flows/TaskEditorSheet.swift +0 -61
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Models/DomainModels.swift +0 -238
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/AnalyticsView.swift +0 -94
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/SettingsView.swift +0 -76
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Screens/TasksHomeView.swift +0 -364
- package/examples/todo-orbit/generated/ios/Todo Orbit/Sources/TodoOrbit/Support/AppModel.swift +0 -324
- package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.pbxproj +0 -439
- package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- package/examples/todo-orbit/generated/ios/Todo Orbit/TodoOrbit.xcodeproj/xcshareddata/xcschemes/TodoOrbit.xcscheme +0 -89
- package/examples/todo-orbit/generated/ios/Todo Orbit/project.yml +0 -32
- package/examples/todo-orbit/generated/web/Todo Orbit/index.html +0 -16
- package/examples/todo-orbit/generated/web/Todo Orbit/package-lock.json +0 -1087
- package/examples/todo-orbit/generated/web/Todo Orbit/package.json +0 -24
- package/examples/todo-orbit/generated/web/Todo Orbit/src/App.tsx +0 -2167
- package/examples/todo-orbit/generated/web/Todo Orbit/src/main.tsx +0 -13
- package/examples/todo-orbit/generated/web/Todo Orbit/src/styles.css +0 -926
- package/examples/todo-orbit/generated/web/Todo Orbit/tsconfig.json +0 -19
- package/examples/todo-orbit/generated/web/Todo Orbit/vite.config.ts +0 -6
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"nav.tasks": "Tasks",
|
|
3
|
-
"nav.projects": "Projects",
|
|
4
|
-
"nav.calendar": "Calendar",
|
|
5
|
-
"nav.settings": "Settings",
|
|
6
|
-
"home.search_label": "Search tasks",
|
|
7
|
-
"home.search_placeholder": "Search by title, tag, or project...",
|
|
8
|
-
"home.filter.all": "All",
|
|
9
|
-
"home.filter.today": "Today",
|
|
10
|
-
"home.filter.upcoming": "Upcoming",
|
|
11
|
-
"home.filter.done": "Done",
|
|
12
|
-
"home.empty_title": "All caught up!",
|
|
13
|
-
"home.empty_body": "No tasks match this filter. Tap + to add one.",
|
|
14
|
-
"home.new_task": "New task",
|
|
15
|
-
"home.mark_complete": "Mark {title} complete",
|
|
16
|
-
"home.task_count.none": "No tasks today",
|
|
17
|
-
"home.task_count.one": "{count} task today",
|
|
18
|
-
"home.task_count.other": "{count} tasks today",
|
|
19
|
-
"task_detail.status": "Status",
|
|
20
|
-
"task_detail.priority": "Priority",
|
|
21
|
-
"task_detail.due": "Due",
|
|
22
|
-
"task_detail.description": "Description",
|
|
23
|
-
"task_detail.details": "Details",
|
|
24
|
-
"task_detail.project": "Project",
|
|
25
|
-
"task_detail.assignee": "Assignee",
|
|
26
|
-
"task_detail.unassigned": "Unassigned",
|
|
27
|
-
"task_detail.tags": "Tags",
|
|
28
|
-
"task_detail.created": "Created",
|
|
29
|
-
"task_detail.edit": "Edit task",
|
|
30
|
-
"task_detail.reopen": "Reopen task",
|
|
31
|
-
"task_detail.complete": "Mark complete",
|
|
32
|
-
"task_detail.delete": "Delete task",
|
|
33
|
-
"task_detail.delete_title": "Delete task?",
|
|
34
|
-
"task_detail.delete_message": "This action cannot be undone. The task \"{title}\" will be permanently removed.",
|
|
35
|
-
"task_detail.task_updated": "Task updated",
|
|
36
|
-
"task_detail.task_deleted": "Task deleted",
|
|
37
|
-
"task_detail.assign_to": "Assign to",
|
|
38
|
-
"create_task.title": "New task",
|
|
39
|
-
"create_task.save": "Save",
|
|
40
|
-
"create_task.saving": "Saving...",
|
|
41
|
-
"create_task.field_title": "Title",
|
|
42
|
-
"create_task.field_title_placeholder": "What needs to be done?",
|
|
43
|
-
"create_task.field_description": "Description",
|
|
44
|
-
"create_task.field_description_placeholder": "Add details, notes, or context...",
|
|
45
|
-
"create_task.field_project": "Project",
|
|
46
|
-
"create_task.field_project_placeholder": "Select a project",
|
|
47
|
-
"create_task.field_priority": "Priority",
|
|
48
|
-
"create_task.field_due_date": "Due date",
|
|
49
|
-
"create_task.field_due_date_placeholder": "No due date",
|
|
50
|
-
"create_task.field_tags": "Tags",
|
|
51
|
-
"create_task.field_tags_placeholder": "Add tags separated by commas",
|
|
52
|
-
"create_task.field_tags_helper": "Press comma or Enter to add a tag",
|
|
53
|
-
"create_task.field_assign_to_me": "Assign to me",
|
|
54
|
-
"create_task.success": "Task created",
|
|
55
|
-
"edit_task.title": "Edit task",
|
|
56
|
-
"edit_task.save": "Save",
|
|
57
|
-
"edit_task.field_title": "Title",
|
|
58
|
-
"edit_task.field_description": "Description",
|
|
59
|
-
"edit_task.field_priority": "Priority",
|
|
60
|
-
"edit_task.field_due_date": "Due date",
|
|
61
|
-
"edit_task.success": "Task updated",
|
|
62
|
-
"projects.title": "Projects",
|
|
63
|
-
"projects.new_project": "New project",
|
|
64
|
-
"projects.task_count.none": "No tasks",
|
|
65
|
-
"projects.task_count.one": "{count} task",
|
|
66
|
-
"projects.task_count.other": "{count} tasks",
|
|
67
|
-
"projects.empty_title": "No projects yet",
|
|
68
|
-
"projects.empty_body": "Create a project to organize your tasks.",
|
|
69
|
-
"projects.dialog_title": "New project",
|
|
70
|
-
"projects.field_name": "Project name",
|
|
71
|
-
"projects.field_name_placeholder": "e.g., Product Launch",
|
|
72
|
-
"projects.field_color": "Color",
|
|
73
|
-
"projects.field_icon": "Icon",
|
|
74
|
-
"projects.created": "Project created",
|
|
75
|
-
"settings.preferences": "Preferences",
|
|
76
|
-
"settings.theme": "Theme",
|
|
77
|
-
"settings.theme_system": "System",
|
|
78
|
-
"settings.theme_light": "Light",
|
|
79
|
-
"settings.theme_dark": "Dark",
|
|
80
|
-
"settings.theme_warm": "Warm",
|
|
81
|
-
"settings.default_priority": "Default priority",
|
|
82
|
-
"settings.notifications": "Push notifications",
|
|
83
|
-
"settings.reminders": "Due date reminders",
|
|
84
|
-
"settings.reminders_helper": "Get notified 1 hour before a task is due",
|
|
85
|
-
"settings.data": "Data",
|
|
86
|
-
"settings.export": "Export data",
|
|
87
|
-
"settings.export_success": "Export sent to your email",
|
|
88
|
-
"settings.delete_account": "Delete account",
|
|
89
|
-
"settings.delete_title": "Delete your account?",
|
|
90
|
-
"settings.delete_message": "This will permanently delete your account and all your data. This action cannot be undone.",
|
|
91
|
-
"settings.delete_confirm": "Delete my account",
|
|
92
|
-
"settings.app_version": "TaskFlow v1.0.0",
|
|
93
|
-
"settings.app_credit": "Built with OpenUISpec",
|
|
94
|
-
"profile.change_photo": "Change photo",
|
|
95
|
-
"profile.field_name": "Name",
|
|
96
|
-
"profile.field_email": "Email",
|
|
97
|
-
"profile.save": "Save changes",
|
|
98
|
-
"profile.success": "Profile updated",
|
|
99
|
-
"calendar.title": "Calendar",
|
|
100
|
-
"calendar.coming_soon": "Coming in a future version",
|
|
101
|
-
"priority.low": "Low",
|
|
102
|
-
"priority.medium": "Medium",
|
|
103
|
-
"priority.high": "High",
|
|
104
|
-
"priority.urgent": "Urgent",
|
|
105
|
-
"status.todo": "To do",
|
|
106
|
-
"status.in_progress": "In progress",
|
|
107
|
-
"status.done": "Done",
|
|
108
|
-
"common.cancel": "Cancel",
|
|
109
|
-
"common.delete": "Delete",
|
|
110
|
-
"common.create": "Create"
|
|
111
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import ReactDOM from "react-dom/client";
|
|
3
|
-
import { BrowserRouter } from "react-router-dom";
|
|
4
|
-
import App from "./App";
|
|
5
|
-
import "./styles.css";
|
|
6
|
-
|
|
7
|
-
ReactDOM.createRoot(document.getElementById("root")!).render(
|
|
8
|
-
<React.StrictMode>
|
|
9
|
-
<BrowserRouter>
|
|
10
|
-
<App />
|
|
11
|
-
</BrowserRouter>
|
|
12
|
-
</React.StrictMode>
|
|
13
|
-
);
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { startTransition, useDeferredValue, useEffect } from "react";
|
|
2
|
-
import { useNavigate } from "react-router-dom";
|
|
3
|
-
import { t, filterLabel, greeting, taskCountLabel } from "../i18n";
|
|
4
|
-
import { useAppStore } from "../store";
|
|
5
|
-
import { buildCounts, matchesFilter, matchesQuery } from "../utils";
|
|
6
|
-
import { TaskRow } from "../components/Common";
|
|
7
|
-
import { TaskDetailPanel } from "./TaskDetail";
|
|
8
|
-
import type { Filter, SizeClass } from "../types";
|
|
9
|
-
|
|
10
|
-
export function HomeScreen(props: {
|
|
11
|
-
sizeClass: SizeClass;
|
|
12
|
-
onCreateTask: () => void;
|
|
13
|
-
onEditTask: (taskId: string) => void;
|
|
14
|
-
onAssignTask: (taskId: string) => void;
|
|
15
|
-
}) {
|
|
16
|
-
const tasks = useAppStore((state) => state.tasks);
|
|
17
|
-
const projects = useAppStore((state) => state.projects);
|
|
18
|
-
const searchQuery = useAppStore((state) => state.searchQuery);
|
|
19
|
-
const activeFilter = useAppStore((state) => state.activeFilter);
|
|
20
|
-
const selectedTaskId = useAppStore((state) => state.selectedTaskId);
|
|
21
|
-
const setSearchQuery = useAppStore((state) => state.setSearchQuery);
|
|
22
|
-
const setActiveFilter = useAppStore((state) => state.setActiveFilter);
|
|
23
|
-
const setSelectedTaskId = useAppStore((state) => state.setSelectedTaskId);
|
|
24
|
-
const user = useAppStore((state) => state.user);
|
|
25
|
-
const deferredQuery = useDeferredValue(searchQuery);
|
|
26
|
-
const navigate = useNavigate();
|
|
27
|
-
|
|
28
|
-
const visibleTasks = tasks.filter((task) => matchesFilter(task, activeFilter) && matchesQuery(task, projects, deferredQuery));
|
|
29
|
-
const selectedTask =
|
|
30
|
-
visibleTasks.find((task) => task.id === selectedTaskId) ??
|
|
31
|
-
tasks.find((task) => task.id === selectedTaskId) ??
|
|
32
|
-
visibleTasks[0];
|
|
33
|
-
const counts = buildCounts(tasks);
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
if (props.sizeClass === "expanded" && selectedTask?.id) {
|
|
37
|
-
setSelectedTaskId(selectedTask.id);
|
|
38
|
-
navigate("/tasks", { replace: true });
|
|
39
|
-
}
|
|
40
|
-
}, [props.sizeClass, selectedTask?.id, navigate, setSelectedTaskId]);
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<section className="screen">
|
|
44
|
-
<header className="screen-header">
|
|
45
|
-
<div>
|
|
46
|
-
<p className="eyebrow">TaskFlow</p>
|
|
47
|
-
<h1>{greeting(user.firstName)}</h1>
|
|
48
|
-
<p className="screen-subtitle">{taskCountLabel(counts.today)}</p>
|
|
49
|
-
</div>
|
|
50
|
-
{props.sizeClass !== "compact" ? (
|
|
51
|
-
<button className="primary-button" onClick={props.onCreateTask}>{t("home.new_task")}</button>
|
|
52
|
-
) : null}
|
|
53
|
-
</header>
|
|
54
|
-
|
|
55
|
-
<div className="search-panel">
|
|
56
|
-
<label className="field-label" htmlFor="task-search">{t("home.search_label")}</label>
|
|
57
|
-
<input
|
|
58
|
-
id="task-search"
|
|
59
|
-
value={searchQuery}
|
|
60
|
-
onChange={(event) => startTransition(() => setSearchQuery(event.target.value))}
|
|
61
|
-
placeholder={t("home.search_placeholder")}
|
|
62
|
-
/>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<div className="chip-row">
|
|
66
|
-
{(["all", "today", "upcoming", "done"] as Filter[]).map((filter) => (
|
|
67
|
-
<button
|
|
68
|
-
key={filter}
|
|
69
|
-
className={filter === activeFilter ? "chip active" : "chip"}
|
|
70
|
-
onClick={() => setActiveFilter(filter)}
|
|
71
|
-
>
|
|
72
|
-
{filterLabel(filter)} ({counts[filter]})
|
|
73
|
-
</button>
|
|
74
|
-
))}
|
|
75
|
-
</div>
|
|
76
|
-
|
|
77
|
-
<div className={props.sizeClass === "expanded" ? "home-grid expanded" : "home-grid"}>
|
|
78
|
-
<div className="card-stack">
|
|
79
|
-
{visibleTasks.length === 0 ? (
|
|
80
|
-
<div className="empty-card">
|
|
81
|
-
<h3>{t("home.empty_title")}</h3>
|
|
82
|
-
<p>{t("home.empty_body")}</p>
|
|
83
|
-
</div>
|
|
84
|
-
) : (
|
|
85
|
-
visibleTasks.map((task) => (
|
|
86
|
-
<TaskRow
|
|
87
|
-
key={task.id}
|
|
88
|
-
task={task}
|
|
89
|
-
project={projects.find((project) => project.id === task.projectId)}
|
|
90
|
-
selected={task.id === selectedTask?.id}
|
|
91
|
-
onSelect={() => {
|
|
92
|
-
setSelectedTaskId(task.id);
|
|
93
|
-
if (props.sizeClass === "expanded") navigate("/tasks", { replace: true });
|
|
94
|
-
else navigate(`/tasks/${task.id}`);
|
|
95
|
-
}}
|
|
96
|
-
/>
|
|
97
|
-
))
|
|
98
|
-
)}
|
|
99
|
-
</div>
|
|
100
|
-
|
|
101
|
-
{props.sizeClass === "expanded" && selectedTask ? (
|
|
102
|
-
<TaskDetailPanel taskId={selectedTask.id} onEditTask={props.onEditTask} onAssignTask={props.onAssignTask} />
|
|
103
|
-
) : null}
|
|
104
|
-
</div>
|
|
105
|
-
|
|
106
|
-
{props.sizeClass === "compact" ? (
|
|
107
|
-
<button className="fab-button" onClick={props.onCreateTask}>{t("home.new_task")}</button>
|
|
108
|
-
) : null}
|
|
109
|
-
</section>
|
|
110
|
-
);
|
|
111
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { useNavigate, useParams, Navigate } from "react-router-dom";
|
|
2
|
-
import { t, projectTaskCountLabel } from "../i18n";
|
|
3
|
-
import { useAppStore } from "../store";
|
|
4
|
-
import { TaskRow } from "../components/Common";
|
|
5
|
-
|
|
6
|
-
export function ProjectsScreen(props: { onCreateProject: () => void }) {
|
|
7
|
-
const projects = useAppStore((state) => state.projects);
|
|
8
|
-
const navigate = useNavigate();
|
|
9
|
-
|
|
10
|
-
return (
|
|
11
|
-
<section className="screen">
|
|
12
|
-
<header className="screen-header">
|
|
13
|
-
<div>
|
|
14
|
-
<p className="eyebrow">{t("nav.projects")}</p>
|
|
15
|
-
<h1>{t("projects.title")}</h1>
|
|
16
|
-
</div>
|
|
17
|
-
<button className="secondary-button" onClick={props.onCreateProject}>{t("projects.new_project")}</button>
|
|
18
|
-
</header>
|
|
19
|
-
|
|
20
|
-
{projects.length === 0 ? (
|
|
21
|
-
<div className="empty-card">
|
|
22
|
-
<h3>{t("projects.empty_title")}</h3>
|
|
23
|
-
<p>{t("projects.empty_body")}</p>
|
|
24
|
-
</div>
|
|
25
|
-
) : (
|
|
26
|
-
<div className="project-grid">
|
|
27
|
-
{projects.map((project) => (
|
|
28
|
-
<button key={project.id} className="project-card" onClick={() => navigate(`/projects/${project.id}`)}>
|
|
29
|
-
<span className="project-chip" style={{ backgroundColor: `${project.color}22`, color: project.color }}>
|
|
30
|
-
{project.icon.toUpperCase().slice(0, 2)}
|
|
31
|
-
</span>
|
|
32
|
-
<h3>{project.name}</h3>
|
|
33
|
-
<p>{projectTaskCountLabel(project.taskCount)}</p>
|
|
34
|
-
</button>
|
|
35
|
-
))}
|
|
36
|
-
</div>
|
|
37
|
-
)}
|
|
38
|
-
</section>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function ProjectDetailRoute() {
|
|
43
|
-
const params = useParams();
|
|
44
|
-
const project = useAppStore((state) => state.projects.find((item) => item.id === params.projectId));
|
|
45
|
-
const tasks = useAppStore((state) => state.tasks.filter((task) => task.projectId === params.projectId));
|
|
46
|
-
const navigate = useNavigate();
|
|
47
|
-
|
|
48
|
-
if (!project) return <Navigate to="/projects" replace />;
|
|
49
|
-
|
|
50
|
-
return (
|
|
51
|
-
<section className="screen">
|
|
52
|
-
<div className="detail-card">
|
|
53
|
-
<div className="detail-hero">
|
|
54
|
-
<div>
|
|
55
|
-
<p className="eyebrow">{t("task_detail.project")}</p>
|
|
56
|
-
<h2>{project.name}</h2>
|
|
57
|
-
</div>
|
|
58
|
-
<span className="badge badge-neutral">{projectTaskCountLabel(tasks.length)}</span>
|
|
59
|
-
</div>
|
|
60
|
-
|
|
61
|
-
<div className="card-stack">
|
|
62
|
-
{tasks.length ? (
|
|
63
|
-
tasks.map((task) => (
|
|
64
|
-
<TaskRow
|
|
65
|
-
key={task.id}
|
|
66
|
-
task={task}
|
|
67
|
-
project={project}
|
|
68
|
-
selected={false}
|
|
69
|
-
onSelect={() => navigate(`/tasks/${task.id}`)}
|
|
70
|
-
/>
|
|
71
|
-
))
|
|
72
|
-
) : (
|
|
73
|
-
<div className="empty-card">
|
|
74
|
-
<h3>{t("project_detail.empty_title")}</h3>
|
|
75
|
-
<p>{t("project_detail.empty_body")}</p>
|
|
76
|
-
</div>
|
|
77
|
-
)}
|
|
78
|
-
</div>
|
|
79
|
-
</div>
|
|
80
|
-
</section>
|
|
81
|
-
);
|
|
82
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import { useNavigate } from "react-router-dom";
|
|
3
|
-
import { t, priorityLabel } from "../i18n";
|
|
4
|
-
import { useAppStore } from "../store";
|
|
5
|
-
import { Avatar, Field, SwitchRow } from "../components/Common";
|
|
6
|
-
|
|
7
|
-
export function CalendarScreen() {
|
|
8
|
-
return (
|
|
9
|
-
<section className="screen">
|
|
10
|
-
<div className="empty-card">
|
|
11
|
-
<p className="eyebrow">{t("nav.calendar")}</p>
|
|
12
|
-
<h1>{t("calendar.title")}</h1>
|
|
13
|
-
<p>{t("calendar.coming_soon")}</p>
|
|
14
|
-
</div>
|
|
15
|
-
</section>
|
|
16
|
-
);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function SettingsScreen() {
|
|
20
|
-
const user = useAppStore((state) => state.user);
|
|
21
|
-
const preferences = useAppStore((state) => state.preferences);
|
|
22
|
-
const updatePreferences = useAppStore((state) => state.updatePreferences);
|
|
23
|
-
const setTheme = useAppStore((state) => state.setTheme);
|
|
24
|
-
const navigate = useNavigate();
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<section className="screen">
|
|
28
|
-
<header className="screen-header">
|
|
29
|
-
<div>
|
|
30
|
-
<p className="eyebrow">{t("nav.settings")}</p>
|
|
31
|
-
<h1>{t("settings.preferences")}</h1>
|
|
32
|
-
</div>
|
|
33
|
-
</header>
|
|
34
|
-
|
|
35
|
-
<button className="profile-card" onClick={() => navigate("/profile")}>
|
|
36
|
-
<Avatar name={user.name} />
|
|
37
|
-
<div>
|
|
38
|
-
<strong>{user.name}</strong>
|
|
39
|
-
<p>{user.email}</p>
|
|
40
|
-
</div>
|
|
41
|
-
</button>
|
|
42
|
-
|
|
43
|
-
<div className="settings-card">
|
|
44
|
-
<p className="section-tag">{t("settings.preferences")}</p>
|
|
45
|
-
<Field label={t("settings.theme")}>
|
|
46
|
-
<select value={preferences.theme} onChange={(event) => setTheme(event.target.value as typeof preferences.theme)}>
|
|
47
|
-
<option value="system">{t("settings.theme_system")}</option>
|
|
48
|
-
<option value="light">{t("settings.theme_light")}</option>
|
|
49
|
-
<option value="dark">{t("settings.theme_dark")}</option>
|
|
50
|
-
<option value="warm">{t("settings.theme_warm")}</option>
|
|
51
|
-
</select>
|
|
52
|
-
</Field>
|
|
53
|
-
<Field label={t("settings.default_priority")}>
|
|
54
|
-
<select
|
|
55
|
-
value={preferences.defaultPriority}
|
|
56
|
-
onChange={(event) => updatePreferences({ defaultPriority: event.target.value as typeof preferences.defaultPriority })}
|
|
57
|
-
>
|
|
58
|
-
<option value="low">{priorityLabel("low")}</option>
|
|
59
|
-
<option value="medium">{priorityLabel("medium")}</option>
|
|
60
|
-
<option value="high">{priorityLabel("high")}</option>
|
|
61
|
-
<option value="urgent">{priorityLabel("urgent")}</option>
|
|
62
|
-
</select>
|
|
63
|
-
</Field>
|
|
64
|
-
<SwitchRow
|
|
65
|
-
label={t("settings.notifications")}
|
|
66
|
-
value={preferences.notificationsEnabled}
|
|
67
|
-
onChange={(value) => updatePreferences({ notificationsEnabled: value })}
|
|
68
|
-
/>
|
|
69
|
-
<SwitchRow
|
|
70
|
-
label={t("settings.reminders")}
|
|
71
|
-
helper={t("settings.reminders_helper")}
|
|
72
|
-
value={preferences.remindersEnabled}
|
|
73
|
-
onChange={(value) => updatePreferences({ remindersEnabled: value })}
|
|
74
|
-
/>
|
|
75
|
-
</div>
|
|
76
|
-
|
|
77
|
-
<div className="settings-card">
|
|
78
|
-
<p className="section-tag">{t("settings.data")}</p>
|
|
79
|
-
<button className="secondary-button wide">{t("settings.export")}</button>
|
|
80
|
-
<button className="danger-button wide" onClick={() => window.alert(t("settings.delete_title"))}>
|
|
81
|
-
{t("settings.delete_account")}
|
|
82
|
-
</button>
|
|
83
|
-
</div>
|
|
84
|
-
|
|
85
|
-
<div className="footnote">
|
|
86
|
-
<span>{t("settings.app_version")}</span>
|
|
87
|
-
<span>{t("settings.app_credit")}</span>
|
|
88
|
-
</div>
|
|
89
|
-
</section>
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export function ProfileScreen() {
|
|
94
|
-
const user = useAppStore((state) => state.user);
|
|
95
|
-
const updateProfile = useAppStore((state) => state.updateProfile);
|
|
96
|
-
const navigate = useNavigate();
|
|
97
|
-
const [name, setName] = useState(user.name);
|
|
98
|
-
const [email, setEmail] = useState(user.email);
|
|
99
|
-
|
|
100
|
-
return (
|
|
101
|
-
<section className="screen">
|
|
102
|
-
<div className="settings-card">
|
|
103
|
-
<div className="profile-hero">
|
|
104
|
-
<Avatar name={user.name} large />
|
|
105
|
-
<div>
|
|
106
|
-
<h1>{user.name}</h1>
|
|
107
|
-
<p>{user.email}</p>
|
|
108
|
-
</div>
|
|
109
|
-
</div>
|
|
110
|
-
|
|
111
|
-
<Field label={t("profile.field_name")}>
|
|
112
|
-
<input value={name} onChange={(event) => setName(event.target.value)} />
|
|
113
|
-
</Field>
|
|
114
|
-
<Field label={t("profile.field_email")}>
|
|
115
|
-
<input value={email} onChange={(event) => setEmail(event.target.value)} />
|
|
116
|
-
</Field>
|
|
117
|
-
<div className="action-row">
|
|
118
|
-
<button className="secondary-button" onClick={() => navigate("/settings")}>{t("common.cancel")}</button>
|
|
119
|
-
<button
|
|
120
|
-
className="primary-button"
|
|
121
|
-
onClick={() => {
|
|
122
|
-
updateProfile({ name, email });
|
|
123
|
-
navigate("/settings");
|
|
124
|
-
}}
|
|
125
|
-
>
|
|
126
|
-
{t("profile.save")}
|
|
127
|
-
</button>
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
</section>
|
|
131
|
-
);
|
|
132
|
-
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { Navigate, useNavigate, useParams } from "react-router-dom";
|
|
2
|
-
import { t, statusLabel, priorityLabel, interpolate } from "../i18n";
|
|
3
|
-
import { useAppStore } from "../store";
|
|
4
|
-
import { formatDate } from "../utils";
|
|
5
|
-
import { DetailRow, StatCard } from "../components/Common";
|
|
6
|
-
import type { SizeClass } from "../types";
|
|
7
|
-
|
|
8
|
-
export function TaskDetailRoute(props: {
|
|
9
|
-
sizeClass: SizeClass;
|
|
10
|
-
onEditTask: (taskId: string) => void;
|
|
11
|
-
onAssignTask: (taskId: string) => void;
|
|
12
|
-
}) {
|
|
13
|
-
const params = useParams();
|
|
14
|
-
if (!params.taskId) return <Navigate to="/tasks" replace />;
|
|
15
|
-
if (props.sizeClass === "expanded") return <Navigate to="/tasks" replace />;
|
|
16
|
-
return <TaskDetailPanel taskId={params.taskId} onEditTask={props.onEditTask} onAssignTask={props.onAssignTask} />;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function TaskDetailPanel(props: {
|
|
20
|
-
taskId: string;
|
|
21
|
-
onEditTask: (taskId: string) => void;
|
|
22
|
-
onAssignTask: (taskId: string) => void;
|
|
23
|
-
}) {
|
|
24
|
-
const task = useAppStore((state) => state.tasks.find((item) => item.id === props.taskId));
|
|
25
|
-
const projects = useAppStore((state) => state.projects);
|
|
26
|
-
const toggleTask = useAppStore((state) => state.toggleTask);
|
|
27
|
-
const deleteTask = useAppStore((state) => state.deleteTask);
|
|
28
|
-
const navigate = useNavigate();
|
|
29
|
-
|
|
30
|
-
if (!task) {
|
|
31
|
-
return (
|
|
32
|
-
<div className="detail-card">
|
|
33
|
-
<h2>Task missing</h2>
|
|
34
|
-
<p>The selected task could not be found.</p>
|
|
35
|
-
</div>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const project = projects.find((item) => item.id === task.projectId);
|
|
40
|
-
|
|
41
|
-
return (
|
|
42
|
-
<article className="detail-card">
|
|
43
|
-
<div className="detail-hero">
|
|
44
|
-
<div>
|
|
45
|
-
<p className="eyebrow">{statusLabel(task.status)}</p>
|
|
46
|
-
<h2>{task.title}</h2>
|
|
47
|
-
</div>
|
|
48
|
-
<span className={`badge badge-${task.status}`}>{statusLabel(task.status)}</span>
|
|
49
|
-
</div>
|
|
50
|
-
|
|
51
|
-
<div className="stat-grid">
|
|
52
|
-
<StatCard label={t("task_detail.status")} value={statusLabel(task.status)} />
|
|
53
|
-
<StatCard label={t("task_detail.priority")} value={priorityLabel(task.priority)} />
|
|
54
|
-
<StatCard label={t("task_detail.due")} value={task.dueDate ? formatDate(task.dueDate) : t("task_detail.unassigned")} />
|
|
55
|
-
</div>
|
|
56
|
-
|
|
57
|
-
{task.description ? (
|
|
58
|
-
<section className="detail-section">
|
|
59
|
-
<p className="section-tag">{t("task_detail.description")}</p>
|
|
60
|
-
<p>{task.description}</p>
|
|
61
|
-
</section>
|
|
62
|
-
) : null}
|
|
63
|
-
|
|
64
|
-
{task.attachment ? (
|
|
65
|
-
<section className="detail-section">
|
|
66
|
-
<p className="section-tag">Media</p>
|
|
67
|
-
<div className="media-player">
|
|
68
|
-
<strong>{task.attachment.title}</strong>
|
|
69
|
-
{task.attachment.mediaType === "video" ? (
|
|
70
|
-
<video controls preload="metadata" src={task.attachment.source} />
|
|
71
|
-
) : (
|
|
72
|
-
<audio controls preload="metadata" src={task.attachment.source} />
|
|
73
|
-
)}
|
|
74
|
-
</div>
|
|
75
|
-
</section>
|
|
76
|
-
) : null}
|
|
77
|
-
|
|
78
|
-
<section className="detail-section">
|
|
79
|
-
<p className="section-tag">{t("task_detail.details")}</p>
|
|
80
|
-
<DetailRow label={t("task_detail.project")} value={project?.name ?? "-"} action={() => project && navigate(`/projects/${project.id}`)} />
|
|
81
|
-
<DetailRow label={t("task_detail.assignee")} value={task.assignee?.name ?? t("task_detail.unassigned")} action={() => props.onAssignTask(task.id)} />
|
|
82
|
-
<DetailRow label={t("task_detail.tags")} value={task.tags.join(", ") || "-"} />
|
|
83
|
-
<DetailRow label={t("task_detail.created")} value={formatDate(task.createdAt)} />
|
|
84
|
-
</section>
|
|
85
|
-
|
|
86
|
-
<div className="action-row">
|
|
87
|
-
<button className="primary-button" onClick={() => props.onEditTask(task.id)}>{t("task_detail.edit")}</button>
|
|
88
|
-
<button className="secondary-button" onClick={() => toggleTask(task.id)}>
|
|
89
|
-
{task.status === "done" ? t("task_detail.reopen") : t("task_detail.complete")}
|
|
90
|
-
</button>
|
|
91
|
-
<button
|
|
92
|
-
className="danger-button"
|
|
93
|
-
onClick={() => {
|
|
94
|
-
if (window.confirm(interpolate(t("task_detail.delete_message"), { title: task.title }))) {
|
|
95
|
-
deleteTask(task.id);
|
|
96
|
-
navigate("/tasks");
|
|
97
|
-
}
|
|
98
|
-
}}
|
|
99
|
-
>
|
|
100
|
-
{t("task_detail.delete")}
|
|
101
|
-
</button>
|
|
102
|
-
</div>
|
|
103
|
-
</article>
|
|
104
|
-
);
|
|
105
|
-
}
|