Skip to content

Commit 88f205a

Browse files
committed
Commit all pending workspace changes
1 parent 6323d14 commit 88f205a

11 files changed

Lines changed: 280 additions & 152 deletions

File tree

.DS_Store

6 KB
Binary file not shown.

.github/.DS_Store

6 KB
Binary file not shown.

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
# プロダクト名
2-
<!-- プロダクト名に変更してください -->
1+
# ネボガード(NeboGuard)
32

4-
![プロダクト名](https://kc3.me/cms/wp-content/uploads/2026/02/444e7120d5cdd74aa75f7a94bf8821a5-scaled.png)
3+
![ネボガード(NeboGuard)](https://kc3.me/cms/wp-content/uploads/2026/02/444e7120d5cdd74aa75f7a94bf8821a5-scaled.png)
54
<!-- プロダクト名・イメージ画像を差し変えてください -->
65

76

d76869c4364b9cc4.PNG

20 KB
Loading

frontend/src/app/dashboard/page.tsx

Lines changed: 186 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,51 @@ export default function DashboardPage() {
627627
}
628628
return { targetMs: wakeupTiming.wakeMs };
629629
}, [wakeupTiming]);
630+
const routineTimeline = useMemo(() => {
631+
const nowMs = Date.now();
632+
if (!wakeupTiming) {
633+
return morningRoutine.map((item) => ({
634+
...item,
635+
startMs: null as number | null,
636+
endMs: null as number | null,
637+
status: "upcoming" as const,
638+
}));
639+
}
640+
641+
let cursorMs = wakeupTiming.wakeMs;
642+
const withTime = morningRoutine.map((item) => {
643+
const durationMs = clampMinutes(item.minutes) * 60 * 1000;
644+
const startMs = cursorMs;
645+
const endMs = cursorMs + durationMs;
646+
cursorMs = endMs;
647+
648+
return {
649+
...item,
650+
startMs,
651+
endMs,
652+
status: "upcoming" as const,
653+
};
654+
});
655+
656+
const nextIndex = withTime.findIndex((item) => (item.endMs ?? 0) > nowMs);
657+
if (nextIndex === -1) {
658+
return withTime.map((item) => ({ ...item, status: "finished" as const }));
659+
}
660+
661+
return withTime.map((item, index) => ({
662+
...item,
663+
status:
664+
index < nextIndex
665+
? ("finished" as const)
666+
: index === nextIndex
667+
? ("next" as const)
668+
: ("upcoming" as const),
669+
}));
670+
}, [morningRoutine, wakeupTiming]);
671+
const nextRoutine = useMemo(
672+
() => routineTimeline.find((item) => item.status === "next") ?? null,
673+
[routineTimeline],
674+
);
630675

631676
const startAlarmSound = useCallback(async () => {
632677
clearAlarmTimeout();
@@ -927,7 +972,7 @@ export default function DashboardPage() {
927972
fontSize={{ base: "sm", md: "md" }}
928973
letterSpacing="0.08em"
929974
>
930-
出発まで
975+
出発時刻
931976
</Text>
932977
<Text
933978
fontSize={{ base: "5xl", md: "7xl" }}
@@ -954,6 +999,7 @@ export default function DashboardPage() {
954999
</Text>
9551000
</Text>
9561001
<Text>移動 {transitMinutes}</Text>
1002+
<Text>余裕 {Math.max(0, slack)}</Text>
9571003
</HStack>
9581004
<Text
9591005
color="gray.500"
@@ -966,60 +1012,6 @@ export default function DashboardPage() {
9661012
</Text>
9671013

9681014
<Stack gap={2} mt={2}>
969-
<Text fontSize="xs" color="gray.600">
970-
朝ルーティン(起床から出発まで)合計:{" "}
971-
{routineTotalMinutes}
972-
</Text>
973-
{routineStatus === "loading" ? (
974-
<Text fontSize="xs" color="gray.500">
975-
朝ルーティンを読み込み中...
976-
</Text>
977-
) : null}
978-
979-
<Stack gap={1}>
980-
{morningRoutine.map((item) => (
981-
<HStack key={item.id} gap={2} align="center">
982-
<Box
983-
w="7px"
984-
h="7px"
985-
borderRadius="full"
986-
bg="gray.400"
987-
flexShrink={0}
988-
/>
989-
<Text
990-
fontSize="sm"
991-
color="gray.700"
992-
overflow="hidden"
993-
textOverflow="ellipsis"
994-
whiteSpace="nowrap"
995-
>
996-
{truncateText(item.label, 22)}
997-
</Text>
998-
<Text fontSize="sm" color="gray.500" ml="auto">
999-
{clampMinutes(item.minutes)}
1000-
</Text>
1001-
</HStack>
1002-
))}
1003-
</Stack>
1004-
1005-
<HStack gap={2} flexWrap="wrap">
1006-
<Button
1007-
size="xs"
1008-
variant="outline"
1009-
colorPalette="blue"
1010-
onClick={handleOpenRoutineEditor}
1011-
disabled={routineStatus === "loading"}
1012-
>
1013-
ルーティンを編集
1014-
</Button>
1015-
</HStack>
1016-
1017-
{routineError ? (
1018-
<Text fontSize="xs" color="orange.700">
1019-
{routineError}
1020-
</Text>
1021-
) : null}
1022-
10231015
<HStack gap={3} flexWrap="wrap">
10241016
<Text
10251017
fontSize={{ base: "sm", md: "md" }}
@@ -1105,48 +1097,154 @@ export default function DashboardPage() {
11051097
<GridItem colSpan={{ base: 1, md: 1, xl: 3 }}>
11061098
<Card minH={{ base: "160px", md: "210px" }}>
11071099
<Text fontSize="md" color="gray.500" mb={1}>
1108-
交通
1109-
</Text>
1110-
<Text
1111-
fontSize={{ base: "3xl", md: "4xl" }}
1112-
fontWeight="semibold"
1113-
color="gray.800"
1114-
>
1115-
{transitMinutes}
1116-
<Text
1117-
as="span"
1118-
fontSize="xl"
1119-
color="gray.500"
1120-
fontWeight="normal"
1121-
>
1122-
1123-
</Text>
1100+
朝ルーティン
11241101
</Text>
1125-
<Text
1126-
color="gray.700"
1127-
fontSize={{ base: "md", md: "lg" }}
1128-
fontWeight="semibold"
1129-
mt={1}
1130-
overflow="hidden"
1131-
textOverflow="ellipsis"
1132-
whiteSpace="nowrap"
1133-
>
1134-
{truncateText(transitSummary, 22)}
1102+
<Text fontSize="xs" color="gray.600">
1103+
合計 {routineTotalMinutes}
11351104
</Text>
1105+
{routineStatus === "loading" ? (
1106+
<Text mt={2} color="gray.500" fontSize="sm">
1107+
朝ルーティンを読み込み中...
1108+
</Text>
1109+
) : (
1110+
<Stack gap={2} mt={2}>
1111+
{nextRoutine ? (
1112+
<Stack gap={0}>
1113+
<Text
1114+
fontSize={{ base: "md", md: "lg" }}
1115+
fontWeight="semibold"
1116+
color="gray.800"
1117+
lineHeight={1.3}
1118+
overflow="hidden"
1119+
textOverflow="ellipsis"
1120+
whiteSpace="nowrap"
1121+
>
1122+
次: {truncateText(nextRoutine.label, 16)}
1123+
</Text>
1124+
{nextRoutine.startMs !== null &&
1125+
nextRoutine.endMs !== null ? (
1126+
<Text fontSize="xs" color="gray.500">
1127+
{toJstHHmm(
1128+
new Date(nextRoutine.startMs).toISOString(),
1129+
)}
1130+
{" - "}
1131+
{toJstHHmm(
1132+
new Date(nextRoutine.endMs).toISOString(),
1133+
)}
1134+
</Text>
1135+
) : null}
1136+
</Stack>
1137+
) : (
1138+
<Text fontSize="sm" color="gray.500">
1139+
次のルーティンはありません
1140+
</Text>
1141+
)}
1142+
1143+
<Stack gap={1.5}>
1144+
{routineTimeline.map((item) => (
1145+
<HStack key={item.id} gap={2} align="start">
1146+
<Box
1147+
mt="6px"
1148+
w="6px"
1149+
h="6px"
1150+
borderRadius="full"
1151+
bg={
1152+
item.status === "finished"
1153+
? "gray.300"
1154+
: item.status === "next"
1155+
? "blue.500"
1156+
: "gray.500"
1157+
}
1158+
flexShrink={0}
1159+
/>
1160+
<Stack gap={0} minW={0} flex="1">
1161+
<Text
1162+
fontSize={
1163+
item.status === "next"
1164+
? { base: "sm", md: "md" }
1165+
: "xs"
1166+
}
1167+
fontWeight={
1168+
item.status === "next" ? "semibold" : "medium"
1169+
}
1170+
color={
1171+
item.status === "finished"
1172+
? "gray.400"
1173+
: item.status === "next"
1174+
? "gray.800"
1175+
: "gray.600"
1176+
}
1177+
lineHeight={1.25}
1178+
overflow="hidden"
1179+
textOverflow="ellipsis"
1180+
whiteSpace="nowrap"
1181+
>
1182+
{truncateText(item.label, 18)}
1183+
</Text>
1184+
{item.startMs !== null && item.endMs !== null ? (
1185+
<Text
1186+
fontSize="xs"
1187+
color={
1188+
item.status === "finished"
1189+
? "gray.400"
1190+
: "gray.500"
1191+
}
1192+
>
1193+
{toJstHHmm(
1194+
new Date(item.startMs).toISOString(),
1195+
)}
1196+
{" - "}
1197+
{toJstHHmm(
1198+
new Date(item.endMs).toISOString(),
1199+
)}
1200+
</Text>
1201+
) : null}
1202+
</Stack>
1203+
<Text
1204+
fontSize="xs"
1205+
color={
1206+
item.status === "finished"
1207+
? "gray.400"
1208+
: "gray.500"
1209+
}
1210+
>
1211+
{clampMinutes(item.minutes)}
1212+
</Text>
1213+
</HStack>
1214+
))}
1215+
</Stack>
1216+
1217+
<HStack gap={2} flexWrap="wrap">
1218+
<Button
1219+
size="xs"
1220+
variant="outline"
1221+
colorPalette="blue"
1222+
onClick={handleOpenRoutineEditor}
1223+
>
1224+
ルーティンを編集
1225+
</Button>
1226+
</HStack>
1227+
</Stack>
1228+
)}
1229+
{routineError ? (
1230+
<Text fontSize="xs" color="orange.700" mt={2}>
1231+
{routineError}
1232+
</Text>
1233+
) : null}
11361234
</Card>
11371235
</GridItem>
11381236

11391237
<GridItem colSpan={{ base: 1, md: 1, xl: 3 }}>
11401238
<Card minH={{ base: "160px", md: "210px" }}>
11411239
<Text fontSize="md" color="gray.500" mb={1}>
1142-
余裕
1240+
交通
11431241
</Text>
11441242
<Text
11451243
fontSize={{ base: "3xl", md: "4xl" }}
11461244
fontWeight="semibold"
11471245
color="gray.800"
11481246
>
1149-
{Math.max(0, slack)}
1247+
{transitMinutes}
11501248
<Text
11511249
as="span"
11521250
fontSize="xl"
@@ -1157,29 +1255,18 @@ export default function DashboardPage() {
11571255
</Text>
11581256
</Text>
11591257
<Text
1160-
color="gray.600"
1258+
color="gray.700"
11611259
fontSize={{ base: "md", md: "lg" }}
11621260
mt={1}
11631261
overflow="hidden"
11641262
textOverflow="ellipsis"
11651263
whiteSpace="nowrap"
11661264
>
1167-
{departure}に出れば
1265+
{truncateText(transitSummary, 22)}
1266+
</Text>
1267+
<Text mt={1} color="gray.500" fontSize="sm">
1268+
推奨出発 {departure}
11681269
</Text>
1169-
<Box
1170-
mt={3}
1171-
h="8px"
1172-
bg="gray.200"
1173-
borderRadius="full"
1174-
overflow="hidden"
1175-
>
1176-
<Box
1177-
h="full"
1178-
borderRadius="full"
1179-
w={`${Math.min(100, Math.max(0, (Math.max(0, slack) / 60) * 100))}%`}
1180-
bg={slack < 5 ? "red.400" : "green.500"}
1181-
/>
1182-
</Box>
11831270
</Card>
11841271
</GridItem>
11851272

frontend/src/app/favicon.ico

5.96 KB
Binary file not shown.

frontend/src/app/icon.png

13.9 KB
Loading

frontend/src/app/layout.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ const geistMono = Geist_Mono({
1414
});
1515

1616
export const metadata: Metadata = {
17-
title: "Task Decomposer",
18-
description: "Task decomposition tool for the hackathon project",
17+
title: "ネボガード | NeboGuard",
18+
description:
19+
"ネボガード(NeboGuard)は、予定・移動・天気を統合して朝の判断を支援するアプリです。",
1920
};
2021

2122
export default function RootLayout({

0 commit comments

Comments
 (0)