Skip to content

Commit c44f8ff

Browse files
committed
added events
1 parent 3c45112 commit c44f8ff

File tree

11 files changed

+153
-28
lines changed

11 files changed

+153
-28
lines changed

proto/models/v1/models.proto

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ syntax = "proto3";
22

33
package models.v1;
44

5+
import "google/protobuf/timestamp.proto";
6+
57
message Habit {
68
message DisplayOptions {
79
optional bool hide_chart = 1;
@@ -48,8 +50,15 @@ message Completion {
4850
}
4951
}
5052

53+
message Event {
54+
google.protobuf.Timestamp time = 1;
55+
uint32 previous_count = 2;
56+
uint32 count = 3;
57+
}
58+
5159
uint32 count = 1;
5260
uint32 target = 2;
61+
repeated Event events = 3;
5362
}
5463

5564
message Tag {

src/components/habit/button.tsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import {
88
CompletionSchema,
99
type Completion_ButtonOptions,
1010
Completion_ButtonOptionsSchema,
11+
Completion_EventSchema,
1112
HabitSchema,
1213
} from "@/proto/models/v1/models_pb"
1314
import { clone, create } from "@bufbuild/protobuf"
15+
import { timestampNow } from "@bufbuild/protobuf/wkt"
1416
import { ArrowRight, Check, Plus, Undo2 } from "lucide-react"
1517
import React from "react"
1618
import { toast } from "sonner"
@@ -33,22 +35,30 @@ const applyButton = (
3335
) => {
3436
console.info("apply button", { completion, options, manualValue })
3537

38+
const previousCount = completion.count
39+
40+
let count = 0
3641
switch (options.kind.case) {
3742
case "delta":
38-
completion.count += manualValue ?? options.kind.value
39-
return
43+
count = previousCount + (manualValue ?? options.kind.value)
44+
break
4045
case "percentage":
41-
completion.count += Math.ceil(
42-
((manualValue ?? options.kind.value) / 100) * completion.target,
43-
)
44-
return
46+
count =
47+
previousCount +
48+
Math.ceil(((manualValue ?? options.kind.value) / 100) * completion.target)
49+
break
4550
case "complete":
46-
completion.count = completion.target
47-
return
51+
count = completion.target
52+
break
4853
case "set":
49-
completion.count = manualValue ?? options.kind.value
50-
return
54+
count = manualValue ?? options.kind.value
55+
break
5156
}
57+
58+
completion.count = count
59+
completion.events.push(
60+
create(Completion_EventSchema, { time: timestampNow(), previousCount, count }),
61+
)
5262
}
5363

5464
export const CompletionButton: React.FC<P> = ({ options = defaultOptions, preview, ...rest }) => {

src/components/habit/chart.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { type Completion } from "@/proto/models/v1/models_pb"
66
import React from "react"
77

88
import { HabitDatePicker } from "./date-picker"
9+
import { HabitHistory } from "./history"
910

1011
const rows = Array.from(Array(7).keys())
1112
const columns = Array.from(Array(53).keys())
@@ -89,7 +90,12 @@ export const HabitChart: React.FC<P> = ({ interactive }) => {
8990
</div>
9091
</div>
9192

92-
<div className="flex justify-end">{interactive && <HabitDatePicker />}</div>
93+
{interactive && (
94+
<div className="flex justify-end items-center gap-2">
95+
<HabitHistory />
96+
<HabitDatePicker />
97+
</div>
98+
)}
9399
</div>
94100
)
95101
}

src/components/habit/date-picker.tsx

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,6 @@ export const HabitDatePicker: React.FC = () => {
2525

2626
return (
2727
<div className="flex items-center gap-2">
28-
<Button
29-
variant="ghost"
30-
size="sm"
31-
className="w-fit"
32-
disabled={selectedDate === undefined}
33-
onClick={() => setSelectedDate(undefined)}
34-
>
35-
<Undo2 />
36-
</Button>
37-
3828
<Dialog open={open} onOpenChange={setOpen}>
3929
<DialogTrigger asChild>
4030
<div className="flex items-center gap-2">
@@ -82,6 +72,15 @@ export const HabitDatePicker: React.FC = () => {
8272
</DialogFooter>
8373
</DialogContent>
8474
</Dialog>
75+
<Button
76+
variant="ghost"
77+
size="sm"
78+
className="w-fit"
79+
disabled={selectedDate === undefined}
80+
onClick={() => setSelectedDate(undefined)}
81+
>
82+
<Undo2 />
83+
</Button>
8584
</div>
8685
)
8786
}

src/components/habit/history.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import {
2+
Dialog,
3+
DialogContent,
4+
DialogDescription,
5+
DialogTitle,
6+
DialogTrigger,
7+
} from "@/components/ui/dialog"
8+
import { timestampDate } from "@bufbuild/protobuf/wkt"
9+
import { CalendarClock, History } from "lucide-react"
10+
import React from "react"
11+
12+
import { DateContext } from "../date/context"
13+
import { Button } from "../ui/button"
14+
import { useHabitContext } from "./context"
15+
16+
export const HabitHistory: React.FC = () => {
17+
const today = React.useContext(DateContext)
18+
const { completion, selectedDate } = useHabitContext()
19+
20+
return (
21+
<Dialog>
22+
<DialogTrigger asChild>
23+
<Button variant="ghost" size="sm" disabled={completion.events.length === 0}>
24+
<CalendarClock />
25+
Events
26+
</Button>
27+
</DialogTrigger>
28+
<DialogContent>
29+
<DialogTitle>Completion History</DialogTitle>
30+
<DialogDescription>
31+
{(selectedDate ?? today).toLocaleDateString()}
32+
</DialogDescription>
33+
34+
{completion.events.length === 0 && (
35+
<div className="flex justify-center pt-4 text-muted-foreground">
36+
No events recorded yet.
37+
</div>
38+
)}
39+
40+
<ul>
41+
{completion.events.map((e, idx) => (
42+
<li key={idx}>
43+
<div className="flex justify-between items-center">
44+
<div className="flex items-center gap-2">
45+
<span>{e.previousCount}</span>
46+
<span className="text-muted-foreground"></span>
47+
<span>{e.count}</span>
48+
</div>
49+
<div className="text-xs text-muted-foreground">
50+
{e.time === undefined
51+
? "Time not recorded"
52+
: timestampDate(e.time).toLocaleString()}
53+
</div>
54+
</div>
55+
</li>
56+
))}
57+
</ul>
58+
</DialogContent>
59+
</Dialog>
60+
)
61+
}

src/proto/buf/validate/validate_pb.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
// @generated by protoc-gen-es v2.10.1
15+
// @generated by protoc-gen-es v2.10.2
1616
// @generated from file buf/validate/validate.proto (package buf.validate, syntax proto2)
1717
/* eslint-disable */
1818

src/proto/buf/validate/validate_pb.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/proto/github/v1/exchange_pb.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// @generated by protoc-gen-es v2.10.1
1+
// @generated by protoc-gen-es v2.10.2
22
// @generated from file github/v1/exchange.proto (package github.v1, syntax proto3)
33
/* eslint-disable */
44

src/proto/github/v1/exchange_pb.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// @generated by protoc-gen-es v2.10.1
1+
// @generated by protoc-gen-es v2.10.2
22
// @generated from file github/v1/exchange.proto (package github.v1, syntax proto3)
33
/* eslint-disable */
44

src/proto/models/v1/models_pb.d.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
// @generated by protoc-gen-es v2.10.1
1+
// @generated by protoc-gen-es v2.10.2
22
// @generated from file models/v1/models.proto (package models.v1, syntax proto3)
33
/* eslint-disable */
44

55
import type { GenFile, GenMessage } from "@bufbuild/protobuf/codegenv2";
66
import type { Message } from "@bufbuild/protobuf";
7+
import type { Timestamp } from "@bufbuild/protobuf/wkt";
78

89
/**
910
* Describes the file models/v1/models.proto.
@@ -113,6 +114,11 @@ export declare type Completion = Message<"models.v1.Completion"> & {
113114
* @generated from field: uint32 target = 2;
114115
*/
115116
target: number;
117+
118+
/**
119+
* @generated from field: repeated models.v1.Completion.Event events = 3;
120+
*/
121+
events: Completion_Event[];
116122
};
117123

118124
/**
@@ -170,6 +176,32 @@ export declare type Completion_ButtonOptions = Message<"models.v1.Completion.But
170176
*/
171177
export declare const Completion_ButtonOptionsSchema: GenMessage<Completion_ButtonOptions>;
172178

179+
/**
180+
* @generated from message models.v1.Completion.Event
181+
*/
182+
export declare type Completion_Event = Message<"models.v1.Completion.Event"> & {
183+
/**
184+
* @generated from field: google.protobuf.Timestamp time = 1;
185+
*/
186+
time?: Timestamp;
187+
188+
/**
189+
* @generated from field: uint32 previous_count = 2;
190+
*/
191+
previousCount: number;
192+
193+
/**
194+
* @generated from field: uint32 count = 3;
195+
*/
196+
count: number;
197+
};
198+
199+
/**
200+
* Describes the message models.v1.Completion.Event.
201+
* Use `create(Completion_EventSchema)` to create a new message.
202+
*/
203+
export declare const Completion_EventSchema: GenMessage<Completion_Event>;
204+
173205
/**
174206
* @generated from message models.v1.Tag
175207
*/

0 commit comments

Comments
 (0)