design-system-next 2.8.3 → 2.9.3

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.
@@ -22,12 +22,6 @@ interface Employee {
22
22
  hoursTarget?: number;
23
23
  schedule: EmployeeSchedule[];
24
24
  }
25
-
26
- interface FilterOption {
27
- text: string;
28
- value: string;
29
- }
30
-
31
25
  export interface SelectedShift {
32
26
  employeeId: string;
33
27
  date: string;
@@ -43,18 +37,6 @@ export const calendarPropTypes = {
43
37
  type: Date,
44
38
  default: () => new Date(),
45
39
  },
46
- companyOptions: {
47
- type: Array as PropType<FilterOption[]>,
48
- default: () => [{ text: 'All Companies', value: 'all' }],
49
- },
50
- departmentOptions: {
51
- type: Array as PropType<FilterOption[]>,
52
- default: () => [{ text: 'All Departments', value: 'all' }],
53
- },
54
- branchOptions: {
55
- type: Array as PropType<FilterOption[]>,
56
- default: () => [{ text: 'All Branches', value: 'all' }],
57
- },
58
40
 
59
41
  search: {
60
42
  type: String,
@@ -69,19 +51,6 @@ export const calendarPropTypes = {
69
51
  shift: null,
70
52
  }),
71
53
  },
72
-
73
- selectedCompany: {
74
- type: String,
75
- default: '',
76
- },
77
- selectedDepartment: {
78
- type: String,
79
- default: '',
80
- },
81
- selectedBranch: {
82
- type: String,
83
- default: '',
84
- },
85
54
  loading: {
86
55
  type: Boolean,
87
56
  default: false,
@@ -98,6 +67,10 @@ export const calendarPropTypes = {
98
67
  type: String,
99
68
  default: 'Add Employee',
100
69
  },
70
+ hideAddButton: {
71
+ type: Boolean,
72
+ default: false,
73
+ },
101
74
  };
102
75
 
103
76
  export const calendarEmitTypes = {};
@@ -1,231 +1,205 @@
1
1
  <template>
2
- <SprCard :has-content-padding="false">
3
- <template #header>
4
- <div :class="[getCalendarClasses.borderClasses, getCalendarClasses.headerWrapper]">
5
- <div class="spr-flex spr-items-center spr-gap-size-spacing-3xs">
6
- <div class="spr-flex">
7
- <spr-button variant="tertiary" has-icon @click="prevWeek">
8
- <Icon icon="ph:caret-left-fill" class="spr-text-color-success-base" />
9
- </spr-button>
10
- <spr-button variant="tertiary" has-icon @click="nextWeek">
11
- <Icon icon="ph:caret-right-fill" class="spr-text-color-success-base" />
12
- </spr-button>
13
- </div>
14
- <h2 class="spr-heading-xs">{{ weekRangeDisplay }}</h2>
15
- </div>
16
-
17
- <spr-button variant="secondary" size="small" @click="goToToday"> Today </spr-button>
18
- </div>
19
- </template>
20
-
2
+ <SprCard :has-content-padding="false" class="spr-flex spr-h-full spr-flex-col spr-overflow-hidden">
21
3
  <template #content>
22
4
  <div :class="getCalendarClasses.contentWrapper">
23
- <!-- Filters -->
24
- <slot name="filter">
25
- <div :class="getCalendarClasses.calendarFilter">
26
- <spr-input v-model="searchTerm" placeholder="Search Employee">
27
- <template #icon>
28
- <Icon icon="ph:magnifying-glass" />
29
- </template>
30
- </spr-input>
31
-
32
- <spr-dropdown
33
- id="company-dropdown"
34
- v-model="companyDropDown"
35
- :menu-list="companyOptions"
36
- @update:model-value="(e) => handleFilter('company', e[0])"
37
- >
38
- <!-- @update:model-value="(e) => (selectedCompany = e[0])" -->
39
- <spr-input v-model="selectedCompany" placeholder="Company">
40
- <template #icon>
41
- <Icon icon="ph:caret-down" />
42
- </template>
43
- </spr-input>
44
- </spr-dropdown>
45
-
46
- <spr-dropdown
47
- id="department-dropdown"
48
- v-model="departmentDropDown"
49
- :menu-list="departmentOptions"
50
- @update:model-value="(e) => handleFilter('department', e[0])"
51
- >
52
- <spr-input v-model="selectedDepartment" placeholder="Department">
53
- <template #icon>
54
- <Icon icon="ph:caret-down" />
55
- </template>
56
- </spr-input>
57
- </spr-dropdown>
58
-
59
- <spr-dropdown
60
- id="branch-dropdown"
61
- v-model="branchDropDown"
62
- :menu-list="branchOptions"
63
- @update:model-value="(e) => handleFilter('branch', e[0])"
64
- >
65
- <spr-input v-model="selectedBranch" placeholder="Branch">
66
- <template #icon>
67
- <Icon icon="ph:caret-down" />
68
- </template>
69
- </spr-input>
70
- </spr-dropdown>
5
+ <div :class="[getCalendarClasses.headerWrapper]">
6
+ <div class="spr-flex spr-items-center spr-justify-center spr-gap-size-spacing-3xs">
7
+ <div class="spr-flex">
8
+ <spr-button variant="tertiary" has-icon @click="prevWeek">
9
+ <Icon icon="ph:caret-left-fill" class="spr-text-color-success-base" />
10
+ </spr-button>
11
+ <spr-button variant="tertiary" has-icon @click="nextWeek">
12
+ <Icon icon="ph:caret-right-fill" class="spr-text-color-success-base" />
13
+ </spr-button>
14
+ </div>
15
+ <div class="spr-heading-xs">{{ weekRangeDisplay }}</div>
71
16
  </div>
72
- </slot>
73
17
 
74
- <div class="spr-table-wrapper">
75
- <table :class="getCalendarClasses.calendarTable">
76
- <!-- Calendar Header -->
77
- <thead>
78
- <tr>
79
- <th :class="[getCalendarClasses.borderClasses, getCalendarClasses.tableHeaderEmployeeName]">
80
- Employee Name
81
- </th>
82
- <th
83
- v-for="(date, index) in weekDates"
84
- :key="index"
85
- :class="[getCalendarClasses.borderClasses, getCalendarClasses.tableHeader]"
86
- >
87
- <div :class="getCalendarClasses.headerContent">
88
- <div
89
- :class="[
90
- getCalendarClasses.headerDate,
91
- {
92
- 'spr-background-color-brand-base spr-text-color-inverted-strong': isToday(date),
93
- },
94
- ]"
95
- >
96
- {{ formatDate(date, 'DD') }}
18
+ <spr-button variant="secondary" size="large" @click="goToToday"> Today </spr-button>
19
+ </div>
20
+ <!-- Filters -->
21
+ <slot name="filter" />
22
+
23
+ <div ref="tableBodyRef" class="spr-table-wrapper spr-h-[calc(100vh-12rem)] spr-w-full spr-overflow-auto">
24
+ <div class="spr-pb-size-spacing-lg">
25
+ <table aria-describedby="calendar" :class="[getCalendarClasses.calendarTable, 'spr-relative']">
26
+ <!-- Calendar Header -->
27
+ <thead class="spr-bg-white spr-sticky spr-top-0 spr-z-20">
28
+ <tr>
29
+ <th :class="[getCalendarClasses.tableHeaderEmployeeName, 'spr-sticky spr-left-0']">
30
+ <div :class="getCalendarClasses.headerContent">
31
+ <div>Employee Name</div>
32
+ <div
33
+ :class="['spr-flex spr-cursor-pointer spr-flex-row spr-items-center spr-p-size-spacing-6xs']"
34
+ @click="handleSorting"
35
+ >
36
+ <Icon :icon="getSortIcon" height="16" width="16" :class="[{ 'spr-text-kangkong-700': sort }]" />
37
+ </div>
97
38
  </div>
98
- <div class="spr-body-sm-regular">
99
- {{ formatDate(date, 'ddd').toUpperCase() }}
39
+ </th>
40
+ <th
41
+ v-for="(date, index) in weekDates"
42
+ :key="index"
43
+ :class="[getCalendarClasses.borderClasses, getCalendarClasses.tableHeader]"
44
+ >
45
+ <div :class="getCalendarClasses.headerContent">
46
+ <div
47
+ :class="[
48
+ getCalendarClasses.headerDate,
49
+ {
50
+ 'spr-background-color-brand-base spr-text-color-inverted-strong': isToday(date),
51
+ },
52
+ ]"
53
+ >
54
+ {{ formatDate(date, 'DD') }}
55
+ </div>
56
+ <div class="spr-body-sm-regular">
57
+ {{ formatDate(date, 'ddd').toUpperCase() }}
58
+ </div>
100
59
  </div>
101
- </div>
102
- </th>
103
- </tr>
104
- </thead>
105
- <tbody v-if="employees.length > 0" class="spr-overflow-y-auto">
106
- <tr v-for="employee in employees" :key="employee.id">
107
- <td
108
- :class="[
109
- getCalendarClasses.borderClasses,
110
- 'spr-content-start spr-border-y spr-border-b-0 spr-border-l-0 spr-border-r spr-p-size-spacing-xs',
111
- ]"
112
- >
113
- <div class="spr-flex spr-flex-col spr-gap-size-spacing-3xs spr-overflow-hidden">
114
- <spr-avatar
115
- :src="employee.avatar"
116
- :initial="employee.name"
117
- size="md"
118
- :variant="employee.avatar ? 'image' : 'initial'"
119
- color="tertiary"
120
- />
121
- <div class="spr-label-xs-regular">{{ employee.name }}</div>
122
- <div class="spr-text-color-supporting spr-label-xs-regular spr-uppercase">
123
- {{ employee.position }}
60
+ </th>
61
+ </tr>
62
+ </thead>
63
+ <tbody v-if="employees.length > 0 && !loading" class="spr-overflow-y-auto">
64
+ <tr v-for="employee in employees" :key="employee.id">
65
+ <td
66
+ :class="[
67
+ getCalendarClasses.borderClasses,
68
+ 'spr-bg-white spr-sticky spr-left-0 spr-z-10 spr-content-start spr-border-y spr-border-b-0 spr-border-l-0 spr-border-r spr-p-size-spacing-xs',
69
+ ]"
70
+ >
71
+ <div class="spr-flex spr-flex-col spr-gap-size-spacing-3xs spr-overflow-hidden">
72
+ <spr-avatar
73
+ :src="employee.avatar"
74
+ :initial="employee.name"
75
+ size="md"
76
+ :variant="employee.avatar ? 'image' : 'initial'"
77
+ color="tertiary"
78
+ />
79
+ <div class="spr-label-xs-regular">{{ employee.name }}</div>
80
+ <div class="spr-text-color-supporting spr-label-xs-regular spr-uppercase">
81
+ {{ employee.position }}
82
+ </div>
124
83
  </div>
125
- </div>
126
- <div class="spr-mt-size-spacing-xs">
127
- <spr-lozenge
128
- :label="`${employee.hoursWorked || 0}/${employee.hoursTarget || 48} HRS`"
129
- tone="neutral"
130
- >
131
- <template #icon>
132
- <Icon icon="ph:clock" />
133
- </template>
134
- </spr-lozenge>
135
- </div>
136
- </td>
137
- <td
138
- v-for="(date, index) in weekDates"
139
- :key="index"
140
- :class="[
141
- getCalendarClasses.borderClasses,
142
- 'spr-content-start spr-space-y-size-spacing-3xs spr-border-x spr-border-b-0 spr-border-t spr-p-size-spacing-sm last:spr-border-r-0',
143
- ]"
144
- @mouseover="handleHover(true, index, employee.id)"
145
- @mouseleave="handleHover(false, index, employee.id)"
146
- >
147
- <section
148
- v-if="
149
- employee.schedule[formatDate(date, dateFormat)] &&
150
- employee.schedule[formatDate(date, dateFormat)].length > 0
151
- "
152
- class="spr-flex spr-flex-col spr-justify-start spr-gap-size-spacing-3xs"
84
+ <div v-if="employee.hoursWorked && employee.hoursTarget" class="spr-mt-size-spacing-xs">
85
+ <spr-lozenge
86
+ :label="`${employee.hoursWorked || 0}/${employee.hoursTarget || 48} HRS`"
87
+ tone="neutral"
88
+ >
89
+ <template #icon>
90
+ <Icon icon="ph:clock" />
91
+ </template>
92
+ </spr-lozenge>
93
+ </div>
94
+ </td>
95
+ <td
96
+ v-for="(date, index) in weekDates"
97
+ :key="index"
98
+ :class="[
99
+ getCalendarClasses.borderClasses,
100
+ 'spr-min-w-[180px] spr-content-start spr-space-y-size-spacing-3xs spr-border-x spr-border-b-0 spr-border-t spr-p-size-spacing-sm last:spr-mb-size-spacing-lg last:spr-border-r-0',
101
+ ]"
102
+ @mouseover="handleHover(true, index, employee.id)"
103
+ @mouseleave="handleHover(false, index, employee.id)"
153
104
  >
154
- <div
155
- v-for="(schedule, scheduleIndex) in employee.schedule[formatDate(date, dateFormat)]"
156
- :key="scheduleIndex"
105
+ <section
106
+ v-if="
107
+ employee.schedule[formatDate(date, dateFormat)] &&
108
+ employee.schedule[formatDate(date, dateFormat)].length > 0
109
+ "
110
+ class="spr-flex spr-flex-col spr-justify-start spr-gap-size-spacing-3xs"
157
111
  >
158
112
  <div
159
- v-if="schedule.type === 'restday'"
160
- @click="
161
- onShiftClick({
162
- employeeId: employee.id,
163
- date: formatDate(date, dateFormat),
164
- shift: 'restday',
165
- })
166
- "
113
+ v-for="(schedule, scheduleIndex) in employee.schedule[formatDate(date, dateFormat)]"
114
+ :key="scheduleIndex"
115
+ class="spr-w-full"
167
116
  >
168
- <spr-calendar-cell type="restday" />
117
+ <div
118
+ v-if="schedule.type === 'restday'"
119
+ class="spr-flex spr-flex-col spr-items-center spr-justify-start"
120
+ @click="
121
+ onCellClick({
122
+ employeeId: employee.id,
123
+ date: formatDate(date, dateFormat),
124
+ shift: 'restday',
125
+ })
126
+ "
127
+ >
128
+ <spr-calendar-cell type="restday" fullwidth />
129
+ </div>
130
+ <div
131
+ class="spr-flex spr-flex-col spr-items-center spr-justify-start"
132
+ v-else
133
+ @click="
134
+ onCellClick({
135
+ employeeId: employee.id,
136
+ date: formatDate(date, dateFormat),
137
+ shift: schedule,
138
+ })
139
+ "
140
+ >
141
+ <spr-calendar-cell
142
+ :view-only="false"
143
+ :title="`${schedule.startTime} - ${schedule.endTime}`"
144
+ :description="schedule.location"
145
+ :sub-description="schedule.type"
146
+ fullwidth
147
+ />
148
+ </div>
169
149
  </div>
170
- <div
171
- v-else
172
- @click="
173
- onShiftClick({
174
- employeeId: employee.id,
175
- date: formatDate(date, dateFormat),
176
- shift: schedule,
177
- })
150
+ </section>
151
+
152
+ <section v-if="showAddShift(index, employee.id)">
153
+ <spr-calendar-cell
154
+ status="pending"
155
+ type="exempt"
156
+ :view-only="false"
157
+ fullwidth
158
+ @on-click="
159
+ onCellClick({ employeeId: employee.id, date: formatDate(date, dateFormat), shift: null })
178
160
  "
179
161
  >
180
- <spr-calendar-cell
181
- :view-only="false"
182
- :title="`${schedule.startTime} - ${schedule.endTime}`"
183
- :description="schedule.location"
184
- :sub-description="schedule.type"
185
- />
162
+ <template #prefix>
163
+ <Icon icon="ph:plus" />
164
+ </template>
165
+ <div class="spr-label-xs-medium">Add New Shift</div>
166
+ </spr-calendar-cell>
167
+ </section>
168
+ </td>
169
+ </tr>
170
+ </tbody>
171
+ <tbody v-else>
172
+ <tr v-if="!loading" class="spr-h-full">
173
+ <td :colspan="weekDates.length + 1" class="spr-flex spr-h-full spr-items-center spr-justify-center">
174
+ <slot name="empty-state">
175
+ <SprEmptyState
176
+ size="large"
177
+ :description="emptyStateTitle"
178
+ :sub-description="emptyStateDescription"
179
+ >
180
+ <template v-if="emptyStateButtonText" #button>
181
+ <spr-button tone="success"
182
+ ><Icon icon="ph:plus" @click="$emit('onClickEmptyButton')" />{{
183
+ emptyStateButtonText
184
+ }}</spr-button
185
+ >
186
+ </template>
187
+ </SprEmptyState>
188
+ </slot>
189
+ </td>
190
+ </tr>
191
+ <tr v-else>
192
+ <td :colspan="weekDates.length + 1" class="spr-h-[360px] spr-overflow-hidden">
193
+ <slot name="loading">
194
+ <div class="spr-flex spr-items-center spr-justify-center">
195
+ <Icon size="48" icon="svg-spinners:ring-resize" class="spr-text-color-success-base" />
186
196
  </div>
187
- </div>
188
- </section>
189
-
190
- <section v-if="showAddShift(index, employee.id)">
191
- <spr-calendar-cell
192
- status="pending"
193
- type="exempt"
194
- :view-only="false"
195
- @on-click="
196
- onShiftClick({ employeeId: employee.id, date: formatDate(date, dateFormat), shift: null })
197
- "
198
- >
199
- <template #prefix>
200
- <Icon icon="ph:plus" />
201
- </template>
202
- <div class="spr-label-xs-medium">Add New Shift</div>
203
- </spr-calendar-cell>
204
- </section>
205
- </td>
206
- </tr>
207
- </tbody>
208
- <tbody v-else>
209
- <tr v-if="!loading" class="spr-h-full">
210
- <td :colspan="weekDates.length + 1" class="spr-flex spr-h-full spr-items-center spr-justify-center">
211
- <slot name="empty-state">
212
- <SprEmptyState size="large" :description="emptyStateTitle" :sub-description="emptyStateDescription">
213
- <template v-if="emptyStateButtonText" #button>
214
- <spr-button tone="success"><Icon icon="ph:plus" />{{ emptyStateButtonText }}</spr-button>
215
- </template>
216
- </SprEmptyState>
217
- </slot>
218
- </td>
219
- </tr>
220
- <tr v-else>
221
- <td :colspan="weekDates.length + 1" class="spr-overflow-hidden">
222
- <slot name="loading">
223
- <div class="spr-flex spr-items-center spr-justify-center">Loading...</div>
224
- </slot>
225
- </td>
226
- </tr>
227
- </tbody>
228
- </table>
197
+ </slot>
198
+ </td>
199
+ </tr>
200
+ </tbody>
201
+ </table>
202
+ </div>
229
203
  </div>
230
204
  </div>
231
205
  </template>
@@ -235,10 +209,8 @@
235
209
  <script setup lang="ts">
236
210
  import { Icon } from '@iconify/vue';
237
211
  import SprButton from '@/components/button/button.vue';
238
- import SprInput from '@/components/input/input.vue';
239
212
  import SprAvatar from '@/components/avatar/avatar.vue';
240
213
  import SprCard from '@/components/card/card.vue';
241
- import SprDropdown from '@/components/dropdown/dropdown.vue';
242
214
  import SprLozenge from '@/components/lozenge/lozenge.vue';
243
215
  import SprCalendarCell from '@/components/calendar-cell/calendar-cell.vue';
244
216
  import SprEmptyState from '@/components/empty-state/empty-state.vue';
@@ -250,26 +222,22 @@ const emit = defineEmits(calendarEmitTypes);
250
222
 
251
223
  const {
252
224
  // State
253
- searchTerm,
254
- selectedCompany,
255
- selectedDepartment,
256
- selectedBranch,
257
225
  weekDates,
258
226
  weekRangeDisplay,
259
227
  getCalendarClasses,
260
- companyDropDown,
261
- departmentDropDown,
262
- branchDropDown,
263
228
  dateFormat,
229
+ getSortIcon,
230
+ sort,
231
+ tableBodyRef,
264
232
  // Function
265
233
  formatDate,
266
234
  isToday,
267
235
  prevWeek,
268
236
  nextWeek,
269
237
  goToToday,
270
- onShiftClick,
238
+ onCellClick,
271
239
  handleHover,
272
240
  showAddShift,
273
- handleFilter,
241
+ handleSorting,
274
242
  } = useCalendar(props, emit);
275
243
  </script>