@axium/calendar 0.1.3 → 0.2.0
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/db.json +15 -0
- package/dist/common.d.ts +16 -0
- package/dist/common.js +3 -0
- package/dist/server.js +29 -15
- package/package.json +3 -3
- package/routes/calendar/+page.svelte +56 -16
package/db.json
CHANGED
|
@@ -67,6 +67,21 @@
|
|
|
67
67
|
"attendees:eventId",
|
|
68
68
|
"attendees:userId"
|
|
69
69
|
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"delta": true,
|
|
73
|
+
"alter_tables": {
|
|
74
|
+
"calendars": {
|
|
75
|
+
"add_columns": {
|
|
76
|
+
"color": { "type": "integer" }
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"events": {
|
|
80
|
+
"add_columns": {
|
|
81
|
+
"color": { "type": "integer" }
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
70
85
|
}
|
|
71
86
|
],
|
|
72
87
|
"wipe": ["calendars", "events", "acl.calendars"],
|
package/dist/common.d.ts
CHANGED
|
@@ -54,6 +54,7 @@ export declare const EventData: z.ZodObject<{
|
|
|
54
54
|
end: z.ZodCoercedDate<unknown>;
|
|
55
55
|
isAllDay: z.ZodCoercedBoolean<unknown>;
|
|
56
56
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
57
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
57
58
|
recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
58
59
|
recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
59
60
|
recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -68,6 +69,7 @@ export declare const EventInit: z.ZodObject<{
|
|
|
68
69
|
end: z.ZodCoercedDate<unknown>;
|
|
69
70
|
isAllDay: z.ZodCoercedBoolean<unknown>;
|
|
70
71
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
72
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
71
73
|
recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
72
74
|
recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
73
75
|
recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -89,6 +91,7 @@ export declare const Event: z.ZodObject<{
|
|
|
89
91
|
end: z.ZodCoercedDate<unknown>;
|
|
90
92
|
isAllDay: z.ZodCoercedBoolean<unknown>;
|
|
91
93
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
94
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
92
95
|
recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
93
96
|
recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
94
97
|
recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -117,6 +120,7 @@ export declare const Calendar: z.ZodObject<{
|
|
|
117
120
|
id: z.ZodUUID;
|
|
118
121
|
userId: z.ZodUUID;
|
|
119
122
|
created: z.ZodCoercedDate<unknown>;
|
|
123
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
120
124
|
acl: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
121
125
|
itemId: z.ZodUUID;
|
|
122
126
|
userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -157,6 +161,7 @@ declare const CalendarAPI: {
|
|
|
157
161
|
id: z.ZodUUID;
|
|
158
162
|
userId: z.ZodUUID;
|
|
159
163
|
created: z.ZodCoercedDate<unknown>;
|
|
164
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
160
165
|
acl: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
161
166
|
itemId: z.ZodUUID;
|
|
162
167
|
userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -185,6 +190,7 @@ declare const CalendarAPI: {
|
|
|
185
190
|
id: z.ZodUUID;
|
|
186
191
|
userId: z.ZodUUID;
|
|
187
192
|
created: z.ZodCoercedDate<unknown>;
|
|
193
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
188
194
|
acl: z.ZodNonOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
189
195
|
itemId: z.ZodUUID;
|
|
190
196
|
userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -215,6 +221,7 @@ declare const CalendarAPI: {
|
|
|
215
221
|
id: z.ZodUUID;
|
|
216
222
|
userId: z.ZodUUID;
|
|
217
223
|
created: z.ZodCoercedDate<unknown>;
|
|
224
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
218
225
|
acl: z.ZodNonOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
219
226
|
itemId: z.ZodUUID;
|
|
220
227
|
userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -245,6 +252,7 @@ declare const CalendarAPI: {
|
|
|
245
252
|
id: z.ZodUUID;
|
|
246
253
|
userId: z.ZodUUID;
|
|
247
254
|
created: z.ZodCoercedDate<unknown>;
|
|
255
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
248
256
|
acl: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
249
257
|
itemId: z.ZodUUID;
|
|
250
258
|
userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -273,6 +281,7 @@ declare const CalendarAPI: {
|
|
|
273
281
|
id: z.ZodUUID;
|
|
274
282
|
userId: z.ZodUUID;
|
|
275
283
|
created: z.ZodCoercedDate<unknown>;
|
|
284
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
276
285
|
acl: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
277
286
|
itemId: z.ZodUUID;
|
|
278
287
|
userId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -309,6 +318,7 @@ declare const CalendarAPI: {
|
|
|
309
318
|
end: z.ZodCoercedDate<unknown>;
|
|
310
319
|
isAllDay: z.ZodCoercedBoolean<unknown>;
|
|
311
320
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
321
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
312
322
|
recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
313
323
|
recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
314
324
|
recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -331,6 +341,7 @@ declare const CalendarAPI: {
|
|
|
331
341
|
end: z.ZodCoercedDate<unknown>;
|
|
332
342
|
isAllDay: z.ZodCoercedBoolean<unknown>;
|
|
333
343
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
344
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
334
345
|
recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
335
346
|
recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
336
347
|
recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -349,6 +360,7 @@ declare const CalendarAPI: {
|
|
|
349
360
|
end: z.ZodCoercedDate<unknown>;
|
|
350
361
|
isAllDay: z.ZodCoercedBoolean<unknown>;
|
|
351
362
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
363
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
352
364
|
recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
353
365
|
recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
354
366
|
recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -373,6 +385,7 @@ declare const CalendarAPI: {
|
|
|
373
385
|
end: z.ZodCoercedDate<unknown>;
|
|
374
386
|
isAllDay: z.ZodCoercedBoolean<unknown>;
|
|
375
387
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
388
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
376
389
|
recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
377
390
|
recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
378
391
|
recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -395,6 +408,7 @@ declare const CalendarAPI: {
|
|
|
395
408
|
end: z.ZodCoercedDate<unknown>;
|
|
396
409
|
isAllDay: z.ZodCoercedBoolean<unknown>;
|
|
397
410
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
411
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
398
412
|
recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
399
413
|
recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
400
414
|
recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -413,6 +427,7 @@ declare const CalendarAPI: {
|
|
|
413
427
|
end: z.ZodCoercedDate<unknown>;
|
|
414
428
|
isAllDay: z.ZodCoercedBoolean<unknown>;
|
|
415
429
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
430
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
416
431
|
recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
417
432
|
recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
418
433
|
recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
|
@@ -435,6 +450,7 @@ declare const CalendarAPI: {
|
|
|
435
450
|
end: z.ZodCoercedDate<unknown>;
|
|
436
451
|
isAllDay: z.ZodCoercedBoolean<unknown>;
|
|
437
452
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
453
|
+
color: z.ZodOptional<z.ZodNullable<z.ZodCustomStringFormat<"hex">>>;
|
|
438
454
|
recurrence: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
439
455
|
recurrenceExcludes: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
440
456
|
recurrenceId: z.ZodOptional<z.ZodNullable<z.ZodUUID>>;
|
package/dist/common.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { $API, AccessControl, pickPermissions } from '@axium/core';
|
|
2
2
|
import * as z from 'zod';
|
|
3
3
|
import $pkg from '../package.json' with { type: 'json' };
|
|
4
|
+
import { Color } from '@axium/core/color';
|
|
4
5
|
export function dayOfYear(date) {
|
|
5
6
|
const yearStart = new Date(date.getFullYear(), 0, 1);
|
|
6
7
|
return Math.round((date.getTime() - yearStart.getTime()) / 86400000 + 1);
|
|
@@ -81,6 +82,7 @@ export const EventData = z.object({
|
|
|
81
82
|
end: z.coerce.date(),
|
|
82
83
|
isAllDay: z.coerce.boolean(),
|
|
83
84
|
description: z.string().max(2000).nullish(),
|
|
85
|
+
color: Color.nullish(),
|
|
84
86
|
// note: recurrences are not support yet
|
|
85
87
|
recurrence: z.string().max(1000).nullish(),
|
|
86
88
|
recurrenceExcludes: z.string().max(100).array().max(100).nullish(),
|
|
@@ -111,6 +113,7 @@ export const Calendar = CalendarInit.extend({
|
|
|
111
113
|
id: z.uuid(),
|
|
112
114
|
userId: z.uuid(),
|
|
113
115
|
created: z.coerce.date(),
|
|
116
|
+
color: Color.nullish(),
|
|
114
117
|
acl: AccessControl.array().optional(),
|
|
115
118
|
});
|
|
116
119
|
export function getCalPermissionsInfo(cal, user) {
|
package/dist/server.js
CHANGED
|
@@ -7,28 +7,37 @@ import { sql } from 'kysely';
|
|
|
7
7
|
import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
|
|
8
8
|
import * as z from 'zod';
|
|
9
9
|
import { Attendee, CalendarInit, EventFilter, EventInit } from './common.js';
|
|
10
|
+
function withEncoded(value) {
|
|
11
|
+
return (value.color === undefined || value.color === null
|
|
12
|
+
? value
|
|
13
|
+
: Object.assign(value, { color: value.color.toString(16).padStart(8, '0') }));
|
|
14
|
+
}
|
|
15
|
+
function withDecoded(value) {
|
|
16
|
+
return (value.color === undefined || value.color === null ? value : Object.assign(value, { color: parseInt(value.color, 16) }));
|
|
17
|
+
}
|
|
10
18
|
addRoute({
|
|
11
19
|
path: '/api/users/:id/calendars',
|
|
12
20
|
params: { id: z.uuid() },
|
|
13
21
|
async GET(request, { id: userId }) {
|
|
14
22
|
const { user } = await checkAuthForUser(request, userId);
|
|
15
|
-
|
|
23
|
+
const result = await database
|
|
16
24
|
.selectFrom('calendars')
|
|
17
25
|
.selectAll()
|
|
18
26
|
.select(aclFrom('calendars'))
|
|
19
27
|
.where(userHasAccess('calendars', user))
|
|
20
28
|
.execute()
|
|
21
29
|
.catch(withError('Could not get calendars'));
|
|
30
|
+
return result.map(c => withEncoded(c));
|
|
22
31
|
},
|
|
23
32
|
async PUT(request, { id: userId }) {
|
|
24
33
|
const init = await parseBody(request, CalendarInit);
|
|
25
34
|
await checkAuthForUser(request, userId);
|
|
26
|
-
return await database
|
|
35
|
+
return withEncoded(await database
|
|
27
36
|
.insertInto('calendars')
|
|
28
37
|
.values({ ...init, userId })
|
|
29
38
|
.returningAll()
|
|
30
39
|
.executeTakeFirstOrThrow()
|
|
31
|
-
.catch(withError('Could not create calendar'));
|
|
40
|
+
.catch(withError('Could not create calendar')));
|
|
32
41
|
},
|
|
33
42
|
});
|
|
34
43
|
addRoute({
|
|
@@ -36,27 +45,27 @@ addRoute({
|
|
|
36
45
|
params: { id: z.uuid() },
|
|
37
46
|
async GET(request, { id }) {
|
|
38
47
|
const { item } = await authRequestForItem(request, 'calendars', id, { read: true });
|
|
39
|
-
return item;
|
|
48
|
+
return withEncoded(item);
|
|
40
49
|
},
|
|
41
50
|
async PATCH(request, { id }) {
|
|
42
51
|
const body = await parseBody(request, CalendarInit);
|
|
43
52
|
await authRequestForItem(request, 'calendars', id, { edit: true });
|
|
44
|
-
return await database
|
|
53
|
+
return withEncoded(await database
|
|
45
54
|
.updateTable('calendars')
|
|
46
55
|
.set(body)
|
|
47
56
|
.where('id', '=', id)
|
|
48
57
|
.returningAll()
|
|
49
58
|
.executeTakeFirstOrThrow()
|
|
50
|
-
.catch(withError('Could not update calendar'));
|
|
59
|
+
.catch(withError('Could not update calendar')));
|
|
51
60
|
},
|
|
52
61
|
async DELETE(request, { id }) {
|
|
53
62
|
await authRequestForItem(request, 'calendars', id, { manage: true });
|
|
54
|
-
return await database
|
|
63
|
+
return withEncoded(await database
|
|
55
64
|
.deleteFrom('calendars')
|
|
56
65
|
.where('id', '=', id)
|
|
57
66
|
.returningAll()
|
|
58
67
|
.executeTakeFirstOrThrow()
|
|
59
|
-
.catch(withError('Could not delete calendar'));
|
|
68
|
+
.catch(withError('Could not delete calendar')));
|
|
60
69
|
},
|
|
61
70
|
});
|
|
62
71
|
function withAttendees(eb) {
|
|
@@ -81,9 +90,10 @@ addRoute({
|
|
|
81
90
|
.where(sql `(${sql.ref('start')}, ${sql.ref('end')}) OVERLAPS (${sql.val(filter.start)}, ${sql.val(filter.end)})`)
|
|
82
91
|
.limit(1000)
|
|
83
92
|
.execute()
|
|
93
|
+
.then(result => result.map(withEncoded))
|
|
84
94
|
.catch(withError('Could not get events'));
|
|
85
95
|
for (const event of events)
|
|
86
|
-
event.calendar = calendar;
|
|
96
|
+
event.calendar = withEncoded(calendar);
|
|
87
97
|
return events;
|
|
88
98
|
},
|
|
89
99
|
async PUT(request, { id }) {
|
|
@@ -93,7 +103,7 @@ addRoute({
|
|
|
93
103
|
try {
|
|
94
104
|
const event = await tx
|
|
95
105
|
.insertInto('events')
|
|
96
|
-
.values({ ...init, calId: id })
|
|
106
|
+
.values({ ...withDecoded(init), calId: id })
|
|
97
107
|
.returningAll()
|
|
98
108
|
.executeTakeFirstOrThrow()
|
|
99
109
|
.catch(withError('Could not create event'));
|
|
@@ -105,7 +115,11 @@ addRoute({
|
|
|
105
115
|
.execute()
|
|
106
116
|
: []);
|
|
107
117
|
await tx.commit().execute();
|
|
108
|
-
return Object.assign(event, {
|
|
118
|
+
return Object.assign(event, {
|
|
119
|
+
attendees,
|
|
120
|
+
calendar: withEncoded(calendar),
|
|
121
|
+
color: event.color?.toString(16)?.padStart(8, '0'),
|
|
122
|
+
});
|
|
109
123
|
}
|
|
110
124
|
catch (e) {
|
|
111
125
|
await tx.rollback().execute();
|
|
@@ -128,7 +142,7 @@ addRoute({
|
|
|
128
142
|
.catch(withError('Event does not exist', 404));
|
|
129
143
|
if (event.calendar.userId != userId && event.attendees.every(a => a.userId != userId))
|
|
130
144
|
error(403, 'You do not have access to this event');
|
|
131
|
-
return event;
|
|
145
|
+
return withEncoded(event);
|
|
132
146
|
},
|
|
133
147
|
async PATCH(request, { id }) {
|
|
134
148
|
const { attendees: attendeesInit = [], sendEmails, recurrenceUpdateAll, ...init } = await parseBody(request, EventInit);
|
|
@@ -145,7 +159,7 @@ addRoute({
|
|
|
145
159
|
try {
|
|
146
160
|
const event = await tx
|
|
147
161
|
.updateTable('events')
|
|
148
|
-
.set(init)
|
|
162
|
+
.set(withDecoded(init))
|
|
149
163
|
.where('id', '=', id)
|
|
150
164
|
.returningAll()
|
|
151
165
|
.returning(withAttendees)
|
|
@@ -173,7 +187,7 @@ addRoute({
|
|
|
173
187
|
*/
|
|
174
188
|
}
|
|
175
189
|
await tx.commit().execute();
|
|
176
|
-
return event;
|
|
190
|
+
return withEncoded(event);
|
|
177
191
|
}
|
|
178
192
|
catch (e) {
|
|
179
193
|
await tx.rollback().execute();
|
|
@@ -198,7 +212,7 @@ addRoute({
|
|
|
198
212
|
.where('id', '=', id)
|
|
199
213
|
.executeTakeFirstOrThrow();
|
|
200
214
|
await tx.commit().execute();
|
|
201
|
-
return event;
|
|
215
|
+
return withEncoded(event);
|
|
202
216
|
}
|
|
203
217
|
catch (e) {
|
|
204
218
|
await tx.rollback().execute();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/calendar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"author": "James Prevett <axium@jamespre.dev>",
|
|
5
5
|
"description": "Calendar for Axium",
|
|
6
6
|
"funding": {
|
|
@@ -36,10 +36,10 @@
|
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@axium/client": ">=0.14.3",
|
|
39
|
-
"@axium/core": ">=0.
|
|
39
|
+
"@axium/core": ">=0.20.0",
|
|
40
40
|
"@axium/server": ">=0.36.0",
|
|
41
41
|
"@sveltejs/kit": "^2.27.3",
|
|
42
|
-
"utilium": "^2.
|
|
42
|
+
"utilium": "^2.7.0"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"zod": "^4.0.5"
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { getEvents, type EventInitFormData } from '@axium/calendar/client';
|
|
3
|
-
import type { Event, EventData } from '@axium/calendar/common';
|
|
3
|
+
import type { Calendar, Event, EventData } from '@axium/calendar/common';
|
|
4
4
|
import { AttendeeInit, dateToInputValue, formatEventTimes, getCalPermissionsInfo, weekDaysFor } from '@axium/calendar/common';
|
|
5
|
-
import * as
|
|
5
|
+
import * as Cal from '@axium/calendar/components';
|
|
6
6
|
import { contextMenu, dynamicRows } from '@axium/client/attachments';
|
|
7
|
-
import { AccessControlDialog, FormDialog, Icon, Popover, UserDiscovery } from '@axium/client/components';
|
|
7
|
+
import { AccessControlDialog, ColorPicker, FormDialog, Icon, Popover, UserDiscovery } from '@axium/client/components';
|
|
8
8
|
import { fetchAPI } from '@axium/client/requests';
|
|
9
|
+
import { colorHashHex, encodeColor, decodeColor } from '@axium/core/color';
|
|
9
10
|
import { SvelteDate } from 'svelte/reactivity';
|
|
10
11
|
import { _throw } from 'utilium';
|
|
11
12
|
import z from 'zod';
|
|
@@ -13,8 +14,9 @@
|
|
|
13
14
|
|
|
14
15
|
const { user } = data.session;
|
|
15
16
|
|
|
16
|
-
const
|
|
17
|
-
|
|
17
|
+
const now = new SvelteDate();
|
|
18
|
+
setInterval(() => now.setTime(Date.now()), 60_000);
|
|
19
|
+
const today = $derived(new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0));
|
|
18
20
|
|
|
19
21
|
let start = new SvelteDate(data.filter.start);
|
|
20
22
|
let end = new SvelteDate(data.filter.end);
|
|
@@ -22,6 +24,7 @@
|
|
|
22
24
|
let events = $state<Event[]>([]);
|
|
23
25
|
|
|
24
26
|
$effect(() => {
|
|
27
|
+
for (const cal of calendars) cal.color ||= encodeColor(colorHashHex(cal.name));
|
|
25
28
|
getEvents(calendars, { start: new Date(start.getTime()), end: new Date(end.getTime()) }).then(e => (events = e));
|
|
26
29
|
});
|
|
27
30
|
|
|
@@ -41,16 +44,20 @@
|
|
|
41
44
|
|
|
42
45
|
let dialogs = $state<Record<string, HTMLDialogElement>>({});
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
const defaultEventInit = {
|
|
48
|
+
attendees: [],
|
|
49
|
+
recurrenceExcludes: [],
|
|
50
|
+
recurrenceId: null,
|
|
51
|
+
calId: calendars[0]?.id,
|
|
52
|
+
} as any;
|
|
53
|
+
|
|
54
|
+
let eventInit = $state<EventData & { attendees: AttendeeInit[]; calendar?: Calendar }>(defaultEventInit),
|
|
50
55
|
eventInitStart = $derived(dateToInputValue(eventInit.start)),
|
|
51
56
|
eventInitEnd = $derived(dateToInputValue(eventInit.end)),
|
|
52
57
|
eventEditId = $state<string>(),
|
|
53
58
|
eventEditCalId = $state<string>();
|
|
59
|
+
|
|
60
|
+
const defaultEventColor = $derived((eventInit.calendar || calendars[0])?.color || encodeColor(colorHashHex(user.name)));
|
|
54
61
|
</script>
|
|
55
62
|
|
|
56
63
|
<svelte:head>
|
|
@@ -78,7 +85,7 @@
|
|
|
78
85
|
<span class="label">{weekDays[0].toLocaleString('default', { month: 'long', year: 'numeric' })}</span>
|
|
79
86
|
</div>
|
|
80
87
|
<div id="cal-list">
|
|
81
|
-
<
|
|
88
|
+
<Cal.Select bind:start bind:end />
|
|
82
89
|
<div class="cal-list-header">
|
|
83
90
|
<h4>My Calendars</h4>
|
|
84
91
|
<button style:display="contents" command="show-modal" commandfor="add-calendar">
|
|
@@ -169,7 +176,16 @@
|
|
|
169
176
|
{#snippet toggle()}
|
|
170
177
|
{@const start = event.start.getHours() * 60 + event.start.getMinutes()}
|
|
171
178
|
{@const end = event.end.getHours() * 60 + event.end.getMinutes()}
|
|
172
|
-
<div
|
|
179
|
+
<div
|
|
180
|
+
class="event"
|
|
181
|
+
style:top="{start / 14.4}%"
|
|
182
|
+
style:height="{(end - start) / 14.4}%"
|
|
183
|
+
style="--event-color:{decodeColor(
|
|
184
|
+
event.color ||
|
|
185
|
+
event.calendar!.color ||
|
|
186
|
+
encodeColor(colorHashHex(event.calendar!.name), true)
|
|
187
|
+
)}"
|
|
188
|
+
>
|
|
173
189
|
<span>{event.summary}</span>
|
|
174
190
|
<span class="subtle">{formatEventTimes(event)}</span>
|
|
175
191
|
</div>
|
|
@@ -249,6 +265,10 @@
|
|
|
249
265
|
{/if}
|
|
250
266
|
</Popover>
|
|
251
267
|
{/each}
|
|
268
|
+
|
|
269
|
+
{#if today.getTime() == day.getTime()}
|
|
270
|
+
<div class="now" style:top="{(now.getHours() * 60 + now.getMinutes()) / 14.4}%"></div>
|
|
271
|
+
{/if}
|
|
252
272
|
</div>
|
|
253
273
|
</div>
|
|
254
274
|
{/each}
|
|
@@ -260,7 +280,7 @@
|
|
|
260
280
|
<FormDialog
|
|
261
281
|
id="event-init"
|
|
262
282
|
clearOnCancel
|
|
263
|
-
cancel={() => (eventInit =
|
|
283
|
+
cancel={() => (eventInit = defaultEventInit)}
|
|
264
284
|
submitText={eventEditId ? 'Update' : 'Create'}
|
|
265
285
|
submit={async (data: EventInitFormData) => {
|
|
266
286
|
Object.assign(eventInit, data);
|
|
@@ -322,6 +342,7 @@
|
|
|
322
342
|
<option value={cal.id}>{cal.name}</option>
|
|
323
343
|
{/each}
|
|
324
344
|
</select>
|
|
345
|
+
<ColorPicker bind:value={eventInit.color} defaultValue={defaultEventColor} />
|
|
325
346
|
</div>
|
|
326
347
|
|
|
327
348
|
<div class="attendees-container">
|
|
@@ -567,14 +588,33 @@
|
|
|
567
588
|
.day-content {
|
|
568
589
|
flex-grow: 1;
|
|
569
590
|
position: relative;
|
|
591
|
+
|
|
592
|
+
.now {
|
|
593
|
+
position: absolute;
|
|
594
|
+
width: 100%;
|
|
595
|
+
border-bottom: 1px solid hsl(0 33 var(--fg-light));
|
|
596
|
+
z-index: 9;
|
|
597
|
+
pointer-events: none;
|
|
598
|
+
|
|
599
|
+
&::before {
|
|
600
|
+
content: '';
|
|
601
|
+
position: absolute;
|
|
602
|
+
left: -0.25em;
|
|
603
|
+
top: -0.25em;
|
|
604
|
+
width: 0.5em;
|
|
605
|
+
height: 0.5em;
|
|
606
|
+
border-radius: 50%;
|
|
607
|
+
background-color: hsl(0 33 var(--fg-light));
|
|
608
|
+
}
|
|
609
|
+
}
|
|
570
610
|
}
|
|
571
611
|
|
|
572
612
|
.event {
|
|
573
|
-
width: 100
|
|
613
|
+
width: calc(100% - 0.5em);
|
|
574
614
|
position: absolute;
|
|
575
615
|
border-radius: 0.5em;
|
|
576
616
|
padding: 0.25em;
|
|
577
|
-
background-color: var(--bg-alt);
|
|
617
|
+
background-color: var(--event-color, var(--bg-alt));
|
|
578
618
|
display: flex;
|
|
579
619
|
flex-direction: column;
|
|
580
620
|
align-items: flex-start;
|