classcard-ui 0.2.1515 → 0.2.1517
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/dist/classcard-ui.common.js +303 -137
- package/dist/classcard-ui.common.js.map +1 -1
- package/dist/classcard-ui.umd.js +303 -137
- package/dist/classcard-ui.umd.js.map +1 -1
- package/dist/classcard-ui.umd.min.js +2 -2
- package/dist/classcard-ui.umd.min.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CCalendar/CCalendar.vue +153 -27
- package/src/components/CUpload/CUpload.vue +9 -1
- package/src/stories/CCalendar.stories.js +61 -0
package/package.json
CHANGED
|
@@ -81,47 +81,92 @@
|
|
|
81
81
|
:key="getDateKey(date)"
|
|
82
82
|
type="button"
|
|
83
83
|
:class="{
|
|
84
|
-
'flex h-
|
|
84
|
+
'flex h-16 flex-col items-center py-2 px-1 hover:bg-gray-100 focus:z-10': true,
|
|
85
85
|
'bg-white': isCurrentMonth(date),
|
|
86
|
-
'bg-gray-
|
|
87
|
-
'font-semibold': isSelected(date) || isToday(date),
|
|
88
|
-
'text-white': isSelected(date),
|
|
89
|
-
'text-indigo-600': !isSelected(date) && isToday(date),
|
|
90
|
-
'text-gray-900':
|
|
91
|
-
!isSelected(date) && isCurrentMonth(date) && !isToday(date),
|
|
92
|
-
'text-gray-500':
|
|
93
|
-
!isSelected(date) &&
|
|
94
|
-
!isCurrentMonth(date) &&
|
|
95
|
-
!isToday(date),
|
|
86
|
+
'bg-gray-100': !isCurrentMonth(date),
|
|
96
87
|
}"
|
|
97
88
|
@click="handleDateClick(date)"
|
|
98
89
|
>
|
|
99
90
|
<time
|
|
100
91
|
:datetime="formatForDateTime(date)"
|
|
92
|
+
class="flex h-5 items-center justify-center rounded-full p-1.5 text-sm"
|
|
101
93
|
:class="{
|
|
102
|
-
'
|
|
103
|
-
'
|
|
104
|
-
isSelected(date),
|
|
105
|
-
'
|
|
106
|
-
|
|
94
|
+
'bg-gray-900 font-semibold text-white': isToday(date),
|
|
95
|
+
'bg-indigo-100 font-semibold text-indigo-700':
|
|
96
|
+
isSelected(date) && !isToday(date),
|
|
97
|
+
'text-gray-900':
|
|
98
|
+
!isToday(date) &&
|
|
99
|
+
!isSelected(date) &&
|
|
100
|
+
isCurrentMonth(date),
|
|
101
|
+
'text-gray-400':
|
|
102
|
+
!isToday(date) &&
|
|
103
|
+
!isSelected(date) &&
|
|
104
|
+
!isCurrentMonth(date),
|
|
107
105
|
}"
|
|
108
106
|
>{{ getDayFromDate(date) }}</time
|
|
109
107
|
>
|
|
110
108
|
<span v-if="eventsCount" class="sr-only"
|
|
111
109
|
>{{ getEventsCountForDate(date) }} events</span
|
|
112
110
|
>
|
|
113
|
-
<
|
|
114
|
-
v-if="
|
|
115
|
-
class="
|
|
111
|
+
<div
|
|
112
|
+
v-if="getDayGlyphs(date)"
|
|
113
|
+
class="mt-auto flex h-2 items-center justify-center gap-1"
|
|
116
114
|
>
|
|
117
115
|
<span
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
v-for="(dot, i) in getDayGlyphs(date).dots"
|
|
117
|
+
:key="dot + '-' + i"
|
|
118
|
+
class="flex"
|
|
119
|
+
>
|
|
120
|
+
<c-icon
|
|
121
|
+
v-if="dot === 'cancelled'"
|
|
122
|
+
name="x-mark-solid-v2"
|
|
123
|
+
type="solid"
|
|
124
|
+
viewBox="0 0 24 24"
|
|
125
|
+
class="h-3 w-3 text-red-600"
|
|
126
|
+
cursor-type="cursor-pointer"
|
|
127
|
+
></c-icon>
|
|
128
|
+
<span
|
|
129
|
+
v-else
|
|
130
|
+
class="block h-2 w-2 rounded-full"
|
|
131
|
+
:class="{
|
|
132
|
+
'bg-green-600': dot === 'confirmed',
|
|
133
|
+
'border-2 border-yellow-600': dot === 'awaiting',
|
|
134
|
+
'bg-gray-400': dot === 'unknown',
|
|
135
|
+
}"
|
|
136
|
+
></span>
|
|
137
|
+
</span>
|
|
138
|
+
<span
|
|
139
|
+
v-if="getDayGlyphs(date).extra > 0"
|
|
140
|
+
class="text-xs leading-none text-gray-500"
|
|
141
|
+
>+{{ getDayGlyphs(date).extra }}</span
|
|
142
|
+
>
|
|
143
|
+
</div>
|
|
121
144
|
</button>
|
|
122
145
|
</div>
|
|
123
146
|
</div>
|
|
124
147
|
</div>
|
|
148
|
+
|
|
149
|
+
<div class="flex items-center justify-center gap-x-5 gap-y-1 py-3">
|
|
150
|
+
<div class="flex items-center gap-1.5">
|
|
151
|
+
<span class="block h-2 w-2 rounded-full bg-green-600"></span>
|
|
152
|
+
<p class="text-sm text-gray-500">Confirmed</p>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="flex items-center gap-1.5">
|
|
155
|
+
<span
|
|
156
|
+
class="block h-2 w-2 rounded-full border-2 border-yellow-600"
|
|
157
|
+
></span>
|
|
158
|
+
<p class="text-sm text-gray-500">Awaiting</p>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="flex items-center gap-1.5">
|
|
161
|
+
<c-icon
|
|
162
|
+
name="x-mark-solid-v2"
|
|
163
|
+
type="solid"
|
|
164
|
+
viewBox="0 0 24 24"
|
|
165
|
+
class="h-4 w-4 text-red-600"
|
|
166
|
+
></c-icon>
|
|
167
|
+
<p class="text-sm text-gray-500">Cancelled</p>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
125
170
|
</div>
|
|
126
171
|
<slot name="customCardsRender"></slot>
|
|
127
172
|
<section
|
|
@@ -153,9 +198,24 @@
|
|
|
153
198
|
:key="event.id"
|
|
154
199
|
@click="handleEventClick(event)"
|
|
155
200
|
>
|
|
156
|
-
<div class="
|
|
157
|
-
|
|
158
|
-
|
|
201
|
+
<div class="flex items-start justify-between gap-2">
|
|
202
|
+
<div class="text-sm text-gray-900">
|
|
203
|
+
{{ formatTimeFromDate(event.start) }} -
|
|
204
|
+
{{ formatTimeFromDate(event.end) }}
|
|
205
|
+
</div>
|
|
206
|
+
<div
|
|
207
|
+
class="flex shrink-0 items-center gap-1 text-sm font-medium"
|
|
208
|
+
:class="statusTag(event).color"
|
|
209
|
+
>
|
|
210
|
+
<c-icon
|
|
211
|
+
:name="statusTag(event).icon"
|
|
212
|
+
type="solid"
|
|
213
|
+
:viewBox="statusTag(event).viewBox"
|
|
214
|
+
class="h-5 w-5 flex-shrink-0"
|
|
215
|
+
:class="statusTag(event).iconColor"
|
|
216
|
+
></c-icon>
|
|
217
|
+
{{ statusTag(event).label }}
|
|
218
|
+
</div>
|
|
159
219
|
</div>
|
|
160
220
|
<div
|
|
161
221
|
class="mt-1 flex text-sm font-semibold"
|
|
@@ -364,7 +424,7 @@ export default {
|
|
|
364
424
|
},
|
|
365
425
|
getEventsCountForDate(date) {
|
|
366
426
|
let eventsCountObj = this.eventsCount.find((event) =>
|
|
367
|
-
dayjs(date).isSame(dayjs(event.date), "day")
|
|
427
|
+
dayjs(date).isSame(dayjs(event.date), "day"),
|
|
368
428
|
);
|
|
369
429
|
if (eventsCountObj) {
|
|
370
430
|
return eventsCountObj.eventsCount;
|
|
@@ -372,6 +432,72 @@ export default {
|
|
|
372
432
|
return 0;
|
|
373
433
|
}
|
|
374
434
|
},
|
|
435
|
+
eventStatus(event) {
|
|
436
|
+
if (event.status === 7) return "cancelled";
|
|
437
|
+
if (event.isConfirmed === 0) return "awaiting";
|
|
438
|
+
return "confirmed";
|
|
439
|
+
},
|
|
440
|
+
statusTag(event) {
|
|
441
|
+
switch (this.eventStatus(event)) {
|
|
442
|
+
case "confirmed":
|
|
443
|
+
return {
|
|
444
|
+
icon: "check-circle-solid",
|
|
445
|
+
viewBox: "0 0 20 20",
|
|
446
|
+
color: "text-green-700",
|
|
447
|
+
iconColor: "text-green-600",
|
|
448
|
+
label: "Confirmed",
|
|
449
|
+
};
|
|
450
|
+
case "awaiting":
|
|
451
|
+
return {
|
|
452
|
+
icon: "clock-solid-v2",
|
|
453
|
+
viewBox: "0 0 24 24",
|
|
454
|
+
color: "text-yellow-700",
|
|
455
|
+
iconColor: "text-yellow-600",
|
|
456
|
+
label: "Awaiting",
|
|
457
|
+
};
|
|
458
|
+
case "cancelled":
|
|
459
|
+
default:
|
|
460
|
+
return {
|
|
461
|
+
icon: "x-mark-solid-v2",
|
|
462
|
+
viewBox: "0 0 24 24",
|
|
463
|
+
color: "text-red-700",
|
|
464
|
+
iconColor: "text-red-600",
|
|
465
|
+
label: "Cancelled",
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
getDayGlyphs(date) {
|
|
470
|
+
// Order exceptions first so truncation never hides a cancellation.
|
|
471
|
+
let rank = { cancelled: 0, awaiting: 1, confirmed: 2 };
|
|
472
|
+
// Per-class statuses come from eventsData (spans the visible month), so
|
|
473
|
+
// when it is loaded it is the source of truth — a day with no events
|
|
474
|
+
// gets no glyphs rather than falling back to a count.
|
|
475
|
+
if (this.eventsData) {
|
|
476
|
+
let dayEvents = this.eventsData.filter((event) =>
|
|
477
|
+
dayjs(date).isSame(dayjs(event.start), "day"),
|
|
478
|
+
);
|
|
479
|
+
if (!dayEvents.length) return null;
|
|
480
|
+
let sorted = dayEvents
|
|
481
|
+
.map((event) => this.eventStatus(event))
|
|
482
|
+
.sort((a, b) => rank[a] - rank[b]);
|
|
483
|
+
return {
|
|
484
|
+
dots: sorted.slice(0, sorted.length > 2 ? 1 : 2),
|
|
485
|
+
extra: sorted.length > 2 ? sorted.length - 1 : 0,
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
// eventsData unavailable but counts loaded: grey dot per class + +N.
|
|
489
|
+
let entry = this.eventsCount
|
|
490
|
+
? this.eventsCount.find((event) =>
|
|
491
|
+
dayjs(date).isSame(dayjs(event.date), "day"),
|
|
492
|
+
)
|
|
493
|
+
: null;
|
|
494
|
+
if (!entry || !entry.eventsCount) return null;
|
|
495
|
+
let shown = Math.min(entry.eventsCount, 2);
|
|
496
|
+
return {
|
|
497
|
+
dots: Array(shown).fill("unknown"),
|
|
498
|
+
extra: entry.eventsCount - shown,
|
|
499
|
+
};
|
|
500
|
+
},
|
|
375
501
|
getActiveStartAndEndDates() {
|
|
376
502
|
return {
|
|
377
503
|
startDate: this.daysArray[0],
|
|
@@ -380,7 +506,7 @@ export default {
|
|
|
380
506
|
},
|
|
381
507
|
getEventsForDate(date) {
|
|
382
508
|
return this.eventsData.filter((event) =>
|
|
383
|
-
dayjs(date).isSame(dayjs(event.start), "day")
|
|
509
|
+
dayjs(date).isSame(dayjs(event.start), "day"),
|
|
384
510
|
);
|
|
385
511
|
},
|
|
386
512
|
handleEventClick(event) {
|
|
@@ -15,7 +15,10 @@
|
|
|
15
15
|
</div>
|
|
16
16
|
<button
|
|
17
17
|
type="button"
|
|
18
|
-
class="
|
|
18
|
+
:class="[
|
|
19
|
+
'inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2',
|
|
20
|
+
extraClasses,
|
|
21
|
+
]"
|
|
19
22
|
@click="initFilestack()"
|
|
20
23
|
:id="id"
|
|
21
24
|
v-if="displayMode == 'overlay' && icon"
|
|
@@ -25,6 +28,7 @@
|
|
|
25
28
|
:class="icon.class"
|
|
26
29
|
:name="icon.name"
|
|
27
30
|
:viewBox="icon.viewBox"
|
|
31
|
+
cursorType="cursor-pointer"
|
|
28
32
|
></c-icon>
|
|
29
33
|
{{ buttonText }}
|
|
30
34
|
</button>
|
|
@@ -116,6 +120,10 @@ export default {
|
|
|
116
120
|
id: {
|
|
117
121
|
type: String,
|
|
118
122
|
},
|
|
123
|
+
extraClasses: {
|
|
124
|
+
type: String,
|
|
125
|
+
default: "",
|
|
126
|
+
},
|
|
119
127
|
buttonText: {
|
|
120
128
|
type: String,
|
|
121
129
|
default: "Upload",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import dayjs from "dayjs";
|
|
1
2
|
import { CCalendar } from "../components";
|
|
2
3
|
import "./utils.css";
|
|
3
4
|
|
|
@@ -13,4 +14,64 @@ const Template = (args, { argTypes }) => ({
|
|
|
13
14
|
template: '<c-calendar v-bind="$props" />',
|
|
14
15
|
});
|
|
15
16
|
|
|
17
|
+
// Demo data lives in the current month so activeDate (dayjs()) lands on it.
|
|
18
|
+
const day = (d) => dayjs().date(d).format("YYYY-MM-DD");
|
|
19
|
+
const at = (d, h) => `${day(d)}T${String(h).padStart(2, "0")}:00:00`;
|
|
20
|
+
|
|
21
|
+
const statusFields = {
|
|
22
|
+
confirmed: { status: 1, isConfirmed: 1 },
|
|
23
|
+
awaiting: { status: 1, isConfirmed: 0 },
|
|
24
|
+
cancelled: { status: 7, isConfirmed: 1 },
|
|
25
|
+
};
|
|
26
|
+
const colors = {
|
|
27
|
+
confirmed: "#047857",
|
|
28
|
+
awaiting: "#1D4ED8",
|
|
29
|
+
cancelled: "#DC2626",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// day-of-month -> ordered list of class statuses for that day
|
|
33
|
+
const spec = {
|
|
34
|
+
3: ["confirmed"],
|
|
35
|
+
7: ["cancelled", "confirmed"],
|
|
36
|
+
12: ["awaiting"],
|
|
37
|
+
13: ["cancelled", "awaiting", "confirmed"],
|
|
38
|
+
22: ["cancelled", "awaiting", "confirmed", "confirmed", "confirmed", "confirmed", "confirmed"],
|
|
39
|
+
26: ["cancelled", "cancelled", "confirmed"],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
let id = 0;
|
|
43
|
+
const eventsData = Object.entries(spec).flatMap(([d, list]) =>
|
|
44
|
+
list.map((st, i) => ({
|
|
45
|
+
id: ++id,
|
|
46
|
+
start: at(+d, 9 + i),
|
|
47
|
+
end: at(+d, 10 + i),
|
|
48
|
+
title: `${st[0].toUpperCase() + st.slice(1)} class`,
|
|
49
|
+
color: colors[st],
|
|
50
|
+
duration: 60,
|
|
51
|
+
subject: "Intermediate Course",
|
|
52
|
+
staff: "Jyoti Karna",
|
|
53
|
+
...statusFields[st],
|
|
54
|
+
}))
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Real API shape: only date + count, no statuses.
|
|
58
|
+
const eventsCount = Object.entries(spec).map(([d, list]) => ({
|
|
59
|
+
date: day(+d),
|
|
60
|
+
eventsCount: list.length,
|
|
61
|
+
}));
|
|
62
|
+
|
|
16
63
|
export const Default = Template.bind({});
|
|
64
|
+
Default.args = {
|
|
65
|
+
id: "cal",
|
|
66
|
+
value: day(13),
|
|
67
|
+
eventsCount,
|
|
68
|
+
eventsData,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Status details failed but counts loaded: grey dot per class + +N.
|
|
72
|
+
export const StatusUnavailable = Template.bind({});
|
|
73
|
+
StatusUnavailable.args = {
|
|
74
|
+
id: "cal-no-status",
|
|
75
|
+
value: day(13),
|
|
76
|
+
eventsCount,
|
|
77
|
+
};
|