@happyvertical/smrt-projects 0.30.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.
Files changed (92) hide show
  1. package/AGENTS.md +31 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +97 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/collections/Issues.d.ts +107 -0
  8. package/dist/collections/Issues.d.ts.map +1 -0
  9. package/dist/collections/Projects.d.ts +90 -0
  10. package/dist/collections/Projects.d.ts.map +1 -0
  11. package/dist/collections/PullRequests.d.ts +107 -0
  12. package/dist/collections/PullRequests.d.ts.map +1 -0
  13. package/dist/collections/Repositories.d.ts +77 -0
  14. package/dist/collections/Repositories.d.ts.map +1 -0
  15. package/dist/constants.d.ts +9 -0
  16. package/dist/constants.d.ts.map +1 -0
  17. package/dist/index.d.ts +14 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +2477 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/manifest.json +4193 -0
  22. package/dist/models/Comment.d.ts +77 -0
  23. package/dist/models/Comment.d.ts.map +1 -0
  24. package/dist/models/Issue.d.ts +200 -0
  25. package/dist/models/Issue.d.ts.map +1 -0
  26. package/dist/models/Label.d.ts +63 -0
  27. package/dist/models/Label.d.ts.map +1 -0
  28. package/dist/models/Project.d.ts +183 -0
  29. package/dist/models/Project.d.ts.map +1 -0
  30. package/dist/models/PullRequest.d.ts +114 -0
  31. package/dist/models/PullRequest.d.ts.map +1 -0
  32. package/dist/models/Repository.d.ts +141 -0
  33. package/dist/models/Repository.d.ts.map +1 -0
  34. package/dist/playground.d.ts +2 -0
  35. package/dist/playground.d.ts.map +1 -0
  36. package/dist/playground.js +129 -0
  37. package/dist/playground.js.map +1 -0
  38. package/dist/prompts.d.ts +2 -0
  39. package/dist/prompts.d.ts.map +1 -0
  40. package/dist/smrt-knowledge.json +1956 -0
  41. package/dist/svelte/components/ApprovalActions.svelte +213 -0
  42. package/dist/svelte/components/ApprovalActions.svelte.d.ts +17 -0
  43. package/dist/svelte/components/ApprovalActions.svelte.d.ts.map +1 -0
  44. package/dist/svelte/components/BulkActions.svelte +224 -0
  45. package/dist/svelte/components/BulkActions.svelte.d.ts +14 -0
  46. package/dist/svelte/components/BulkActions.svelte.d.ts.map +1 -0
  47. package/dist/svelte/components/DurationDisplay.svelte +68 -0
  48. package/dist/svelte/components/DurationDisplay.svelte.d.ts +11 -0
  49. package/dist/svelte/components/DurationDisplay.svelte.d.ts.map +1 -0
  50. package/dist/svelte/components/RejectDialog.svelte +250 -0
  51. package/dist/svelte/components/RejectDialog.svelte.d.ts +17 -0
  52. package/dist/svelte/components/RejectDialog.svelte.d.ts.map +1 -0
  53. package/dist/svelte/components/TimeEntryCard.svelte +294 -0
  54. package/dist/svelte/components/TimeEntryCard.svelte.d.ts +17 -0
  55. package/dist/svelte/components/TimeEntryCard.svelte.d.ts.map +1 -0
  56. package/dist/svelte/components/TimeEntryList.svelte +351 -0
  57. package/dist/svelte/components/TimeEntryList.svelte.d.ts +17 -0
  58. package/dist/svelte/components/TimeEntryList.svelte.d.ts.map +1 -0
  59. package/dist/svelte/components/TimeSummary.svelte +165 -0
  60. package/dist/svelte/components/TimeSummary.svelte.d.ts +19 -0
  61. package/dist/svelte/components/TimeSummary.svelte.d.ts.map +1 -0
  62. package/dist/svelte/components/__tests__/ApprovalActions.test.js +41 -0
  63. package/dist/svelte/components/__tests__/BulkActions.test.js +46 -0
  64. package/dist/svelte/components/__tests__/DurationDisplay.test.js +23 -0
  65. package/dist/svelte/components/__tests__/RejectDialog.test.js +45 -0
  66. package/dist/svelte/components/__tests__/TimeEntryCard.test.js +45 -0
  67. package/dist/svelte/components/__tests__/TimeEntryList.test.js +50 -0
  68. package/dist/svelte/components/__tests__/TimeSummary.test.js +39 -0
  69. package/dist/svelte/components/utils.d.ts +42 -0
  70. package/dist/svelte/components/utils.d.ts.map +1 -0
  71. package/dist/svelte/components/utils.js +43 -0
  72. package/dist/svelte/i18n.d.ts +18 -0
  73. package/dist/svelte/i18n.d.ts.map +1 -0
  74. package/dist/svelte/i18n.js +18 -0
  75. package/dist/svelte/index.d.ts +26 -0
  76. package/dist/svelte/index.d.ts.map +1 -0
  77. package/dist/svelte/index.js +31 -0
  78. package/dist/svelte/playground.d.ts +122 -0
  79. package/dist/svelte/playground.d.ts.map +1 -0
  80. package/dist/svelte/playground.js +114 -0
  81. package/dist/svelte/utils.d.ts +42 -0
  82. package/dist/svelte/utils.d.ts.map +1 -0
  83. package/dist/svelte/utils.js +43 -0
  84. package/dist/types.d.ts +54 -0
  85. package/dist/types.d.ts.map +1 -0
  86. package/dist/types.js +2 -0
  87. package/dist/types.js.map +1 -0
  88. package/dist/ui.d.ts +10 -0
  89. package/dist/ui.d.ts.map +1 -0
  90. package/dist/ui.js +85 -0
  91. package/dist/ui.js.map +1 -0
  92. package/package.json +100 -0
@@ -0,0 +1,250 @@
1
+ <script lang="ts">
2
+ /**
3
+ * RejectDialog - Modal dialog for rejecting with a reason
4
+ * Requires a reason to be entered before confirming
5
+ */
6
+
7
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
8
+ import { M } from '../i18n.js';
9
+
10
+ const { t } = useI18n();
11
+
12
+ /** Props for RejectDialog component */
13
+ export interface Props {
14
+ open: boolean;
15
+ title?: string;
16
+ message?: string;
17
+ confirmLabel?: string;
18
+ cancelLabel?: string;
19
+ placeholder?: string;
20
+ required?: boolean;
21
+ loading?: boolean;
22
+ onconfirm: (reason: string) => void;
23
+ oncancel: () => void;
24
+ }
25
+
26
+ let {
27
+ open,
28
+ title = 'Reject Entry',
29
+ message = 'Please provide a reason for rejection:',
30
+ confirmLabel = 'Reject',
31
+ cancelLabel = 'Cancel',
32
+ placeholder = 'Enter rejection reason...',
33
+ required = true,
34
+ loading = false,
35
+ onconfirm,
36
+ oncancel,
37
+ }: Props = $props();
38
+
39
+ let reason = $state('');
40
+ let textarea: HTMLTextAreaElement | undefined = $state(undefined);
41
+
42
+ const canConfirm = $derived(!required || reason.trim().length > 0);
43
+
44
+ function handleConfirm() {
45
+ if (canConfirm) {
46
+ onconfirm(reason.trim());
47
+ reason = '';
48
+ }
49
+ }
50
+
51
+ function handleCancel() {
52
+ reason = '';
53
+ oncancel();
54
+ }
55
+
56
+ function handleKeydown(event: KeyboardEvent) {
57
+ if (event.key === 'Escape') {
58
+ handleCancel();
59
+ }
60
+ }
61
+
62
+ function handleBackdropClick(event: MouseEvent) {
63
+ if (event.target === event.currentTarget) {
64
+ handleCancel();
65
+ }
66
+ }
67
+
68
+ $effect(() => {
69
+ if (open && textarea) {
70
+ textarea.focus();
71
+ }
72
+ });
73
+ </script>
74
+
75
+ {#if open}
76
+ <div class="dialog-backdrop">
77
+ <button
78
+ type="button"
79
+ class="dialog-overlay"
80
+ aria-label={t(M['projects.reject_dialog.close_dialog'])}
81
+ onclick={handleCancel}
82
+ ></button>
83
+ <div
84
+ class="dialog"
85
+ role="dialog"
86
+ aria-modal="true"
87
+ aria-labelledby="reject-dialog-title"
88
+ tabindex="-1"
89
+ onkeydown={handleKeydown}
90
+ >
91
+ <h2 id="reject-dialog-title" class="dialog-title">{title}</h2>
92
+
93
+ <p class="dialog-message">{message}</p>
94
+
95
+ <textarea
96
+ bind:this={textarea}
97
+ bind:value={reason}
98
+ class="reason-input"
99
+ placeholder={placeholder}
100
+ rows="3"
101
+ disabled={loading}
102
+ ></textarea>
103
+
104
+ {#if required && reason.trim().length === 0}
105
+ <p class="hint">{t(M['projects.reject_dialog.reason_required'])}</p>
106
+ {/if}
107
+
108
+ <div class="dialog-actions">
109
+ <button
110
+ type="button"
111
+ class="btn btn-secondary"
112
+ onclick={handleCancel}
113
+ disabled={loading}
114
+ >
115
+ {cancelLabel}
116
+ </button>
117
+ <button
118
+ type="button"
119
+ class="btn btn-error"
120
+ onclick={handleConfirm}
121
+ disabled={!canConfirm || loading}
122
+ >
123
+ {loading ? 'Rejecting...' : confirmLabel}
124
+ </button>
125
+ </div>
126
+ </div>
127
+ </div>
128
+ {/if}
129
+
130
+ <style>
131
+ .dialog-backdrop {
132
+ position: fixed;
133
+ inset: 0;
134
+ display: flex;
135
+ align-items: center;
136
+ justify-content: center;
137
+ padding: 1rem;
138
+ z-index: var(--smrt-z-index-dialog, 1300);
139
+ }
140
+
141
+ .dialog-overlay {
142
+ position: absolute;
143
+ inset: 0;
144
+ border: none;
145
+ background: var(--smrt-color-scrim, rgba(0, 0, 0, 0.32));
146
+ cursor: pointer;
147
+ }
148
+
149
+ .dialog {
150
+ position: relative;
151
+ background: var(--smrt-color-surface);
152
+ border-radius: var(--smrt-radius-extra-large, 28px);
153
+ box-shadow: var(--smrt-elevation-3);
154
+ width: 100%;
155
+ max-width: 28rem;
156
+ padding: 1.5rem;
157
+ }
158
+
159
+ .dialog-title {
160
+ margin: 0 0 0.5rem;
161
+ font-size: var(--smrt-typography-headline-small-size, 1.5rem);
162
+ font-weight: var(--smrt-typography-headline-small-weight, 400);
163
+ color: var(--smrt-color-on-surface);
164
+ line-height: var(--smrt-typography-headline-small-line-height, 2rem);
165
+ }
166
+
167
+ .dialog-message {
168
+ margin: 0 0 1rem;
169
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
170
+ color: var(--smrt-color-on-surface-variant);
171
+ line-height: var(--smrt-typography-body-medium-line-height, 1.25rem);
172
+ }
173
+
174
+ .reason-input {
175
+ width: 100%;
176
+ padding: 0.75rem;
177
+ font-size: var(--smrt-typography-body-large-size, 1rem);
178
+ font-family: inherit;
179
+ border: 1px solid var(--smrt-color-outline);
180
+ border-radius: var(--smrt-radius-small, 8px);
181
+ resize: vertical;
182
+ min-height: 80px;
183
+ background: var(--smrt-color-surface);
184
+ color: var(--smrt-color-on-surface);
185
+ }
186
+
187
+ .reason-input:focus {
188
+ outline: none;
189
+ border-color: var(--smrt-color-primary);
190
+ box-shadow: 0 0 0 1px var(--smrt-color-primary);
191
+ }
192
+
193
+ .reason-input:disabled {
194
+ background: var(--smrt-color-surface-container-highest);
195
+ color: var(--smrt-color-on-surface);
196
+ opacity: 0.38;
197
+ cursor: not-allowed;
198
+ }
199
+
200
+ .hint {
201
+ margin: 0.5rem 0 0;
202
+ font-size: var(--smrt-typography-body-small-size, 0.75rem);
203
+ color: var(--smrt-color-on-surface-variant);
204
+ }
205
+
206
+ .dialog-actions {
207
+ display: flex;
208
+ justify-content: flex-end;
209
+ gap: 0.5rem;
210
+ margin-top: 1.5rem;
211
+ }
212
+
213
+ .btn {
214
+ display: inline-flex;
215
+ align-items: center;
216
+ justify-content: center;
217
+ padding: 0.625rem 1.5rem;
218
+ font-size: var(--smrt-typography-label-large-size, 0.875rem);
219
+ font-weight: var(--smrt-typography-label-large-weight, 500);
220
+ letter-spacing: var(--smrt-typography-label-large-tracking, 0.1px);
221
+ border-radius: var(--smrt-radius-full, 9999px);
222
+ border: none;
223
+ cursor: pointer;
224
+ transition: all 0.2s var(--smrt-easing-standard);
225
+ }
226
+
227
+ .btn:disabled {
228
+ opacity: 0.38;
229
+ cursor: not-allowed;
230
+ }
231
+
232
+ .btn-secondary {
233
+ background: transparent;
234
+ color: var(--smrt-color-primary);
235
+ }
236
+
237
+ .btn-secondary:hover:not(:disabled) {
238
+ background: var(--smrt-color-primary);
239
+ background: color-mix(in srgb, var(--smrt-color-primary) 8%, transparent);
240
+ }
241
+
242
+ .btn-error {
243
+ background: var(--smrt-color-error);
244
+ color: var(--smrt-color-on-error);
245
+ }
246
+
247
+ .btn-error:hover:not(:disabled) {
248
+ box-shadow: var(--smrt-elevation-1);
249
+ }
250
+ </style>
@@ -0,0 +1,17 @@
1
+ /** Props for RejectDialog component */
2
+ export interface Props {
3
+ open: boolean;
4
+ title?: string;
5
+ message?: string;
6
+ confirmLabel?: string;
7
+ cancelLabel?: string;
8
+ placeholder?: string;
9
+ required?: boolean;
10
+ loading?: boolean;
11
+ onconfirm: (reason: string) => void;
12
+ oncancel: () => void;
13
+ }
14
+ declare const RejectDialog: import("svelte").Component<Props, {}, "">;
15
+ type RejectDialog = ReturnType<typeof RejectDialog>;
16
+ export default RejectDialog;
17
+ //# sourceMappingURL=RejectDialog.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RejectDialog.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/RejectDialog.svelte.ts"],"names":[],"mappings":"AAWA,uCAAuC;AACvC,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAyFD,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
@@ -0,0 +1,294 @@
1
+ <script lang="ts">
2
+ /**
3
+ * TimeEntryCard - Card component for displaying time entries
4
+ * Supports selection mode for bulk operations
5
+ */
6
+
7
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
8
+ import type { Snippet } from 'svelte';
9
+ import { M } from '../i18n.js';
10
+ import {
11
+ type Currency,
12
+ formatCurrency,
13
+ formatDate,
14
+ formatHours,
15
+ statusColors,
16
+ type TimeEntry,
17
+ } from './utils.js';
18
+
19
+ /** Props for TimeEntryCard component */
20
+ export interface Props {
21
+ entry: TimeEntry;
22
+ href?: string;
23
+ onclick?: () => void;
24
+ selectable?: boolean;
25
+ selected?: boolean;
26
+ onselect?: (id: string, selected: boolean) => void;
27
+ currency?: Currency;
28
+ actions?: Snippet;
29
+ }
30
+
31
+ let {
32
+ entry,
33
+ href,
34
+ onclick,
35
+ selectable = false,
36
+ selected = false,
37
+ onselect,
38
+ currency = 'CAD',
39
+ actions,
40
+ }: Props = $props();
41
+
42
+ const { t } = useI18n();
43
+
44
+ function handleCheckboxChange(event: Event) {
45
+ const target = event.target as HTMLInputElement;
46
+ onselect?.(entry.id, target.checked);
47
+ }
48
+
49
+ function handleCardClick(event: MouseEvent) {
50
+ // If there's an onclick handler, use it
51
+ if (onclick) {
52
+ event.preventDefault();
53
+ onclick();
54
+ }
55
+ // If href is provided, let the anchor handle navigation naturally
56
+ }
57
+
58
+ function handleKeydown(event: KeyboardEvent) {
59
+ if (event.key === 'Enter' || event.key === ' ') {
60
+ if (onclick) {
61
+ event.preventDefault();
62
+ onclick();
63
+ }
64
+ // For href, let the anchor handle it naturally
65
+ }
66
+ }
67
+ </script>
68
+
69
+ <div
70
+ class="time-entry-card"
71
+ class:selectable
72
+ class:selected
73
+ class:clickable={!!href || !!onclick}
74
+ >
75
+ {#if selectable}
76
+ <div class="checkbox-wrapper">
77
+ <input
78
+ type="checkbox"
79
+ checked={selected}
80
+ onchange={handleCheckboxChange}
81
+ aria-label={t(M['projects.time_entry_card.select_entry'], { description: entry.description })}
82
+ />
83
+ </div>
84
+ {/if}
85
+
86
+ {#if href}
87
+ <a
88
+ {href}
89
+ class="card-content"
90
+ onclick={handleCardClick}
91
+ onkeydown={handleKeydown}
92
+ >
93
+ {@render cardBody()}
94
+ </a>
95
+ {:else if onclick}
96
+ <button
97
+ type="button"
98
+ class="card-content"
99
+ onclick={() => onclick?.()}
100
+ >
101
+ {@render cardBody()}
102
+ </button>
103
+ {:else}
104
+ <div class="card-content">
105
+ {@render cardBody()}
106
+ </div>
107
+ {/if}
108
+
109
+ {#if actions}
110
+ <div class="card-actions">
111
+ {@render actions()}
112
+ </div>
113
+ {/if}
114
+ </div>
115
+
116
+ {#snippet cardBody()}
117
+ <div class="entry-header">
118
+ <span class="date">{formatDate(entry.date)}</span>
119
+ <span class="status-badge" style="--status-color: {statusColors[entry.status]}">
120
+ {entry.status.toUpperCase()}
121
+ </span>
122
+ </div>
123
+
124
+ <div class="entry-body">
125
+ <p class="description">{entry.description}</p>
126
+ {#if entry.workerName}
127
+ <p class="worker">{entry.workerName}</p>
128
+ {/if}
129
+ </div>
130
+
131
+ <div class="entry-footer">
132
+ <div class="hours">
133
+ <span class="hours-value">{formatHours(entry.hours)}</span>
134
+ {#if entry.hourlyRate}
135
+ <span class="rate">@ {formatCurrency(entry.hourlyRate, currency)}/hr</span>
136
+ {/if}
137
+ </div>
138
+ {#if entry.amount !== undefined}
139
+ <span class="amount">{formatCurrency(entry.amount, currency)}</span>
140
+ {/if}
141
+ </div>
142
+
143
+ {#if entry.mileage && entry.mileage > 0}
144
+ <div class="mileage">
145
+ {t(M['projects.time_entry_card.mileage'], { mileage: entry.mileage })}
146
+ </div>
147
+ {/if}
148
+ {/snippet}
149
+
150
+ <style>
151
+ .time-entry-card {
152
+ display: flex;
153
+ align-items: stretch;
154
+ background: var(--smrt-color-surface);
155
+ border: 1px solid var(--smrt-color-outline-variant);
156
+ border-radius: var(--smrt-radius-medium, 12px);
157
+ overflow: hidden;
158
+ transition: all 0.2s var(--smrt-easing-standard);
159
+ }
160
+
161
+ .time-entry-card:hover {
162
+ border-color: var(--smrt-color-outline);
163
+ }
164
+
165
+ .time-entry-card.selected {
166
+ border-color: var(--smrt-color-primary);
167
+ background: var(--smrt-color-primary-container);
168
+ }
169
+
170
+ .time-entry-card.clickable .card-content {
171
+ cursor: pointer;
172
+ }
173
+
174
+ .time-entry-card.clickable:hover {
175
+ box-shadow: var(--smrt-elevation-1);
176
+ }
177
+
178
+ .checkbox-wrapper {
179
+ display: flex;
180
+ align-items: center;
181
+ padding: 1rem;
182
+ background: var(--smrt-color-surface-container-low);
183
+ border-right: 1px solid var(--smrt-color-outline-variant);
184
+ }
185
+
186
+ .checkbox-wrapper input[type='checkbox'] {
187
+ width: 1.25rem;
188
+ height: 1.25rem;
189
+ cursor: pointer;
190
+ accent-color: var(--smrt-color-primary);
191
+ }
192
+
193
+ .card-content {
194
+ flex: 1;
195
+ padding: 1rem;
196
+ min-width: 0;
197
+ text-decoration: none;
198
+ color: inherit;
199
+ background: transparent;
200
+ border: none;
201
+ text-align: left;
202
+ font: inherit;
203
+ display: block;
204
+ width: 100%;
205
+ }
206
+
207
+ a.card-content:focus,
208
+ button.card-content:focus {
209
+ outline: 2px solid var(--smrt-color-primary);
210
+ outline-offset: -2px;
211
+ border-radius: var(--smrt-radius-small, 8px);
212
+ }
213
+
214
+ .entry-header {
215
+ display: flex;
216
+ justify-content: space-between;
217
+ align-items: center;
218
+ margin-bottom: 0.5rem;
219
+ }
220
+
221
+ .date {
222
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
223
+ color: var(--smrt-color-on-surface-variant);
224
+ }
225
+
226
+ .status-badge {
227
+ font-size: var(--smrt-typography-label-small-size, 0.625rem);
228
+ font-weight: var(--smrt-typography-label-small-weight, 500);
229
+ padding: 0.25rem 0.5rem;
230
+ border-radius: var(--smrt-radius-small, 8px);
231
+ background: var(--status-color);
232
+ color: var(--smrt-color-on-primary);
233
+ letter-spacing: var(--smrt-typography-label-small-tracking, 0.5px);
234
+ }
235
+
236
+ .entry-body {
237
+ margin-bottom: 0.75rem;
238
+ }
239
+
240
+ .description {
241
+ margin: 0;
242
+ font-size: var(--smrt-typography-body-large-size, 0.9375rem);
243
+ color: var(--smrt-color-on-surface);
244
+ line-height: var(--smrt-typography-body-large-line-height, 1.5);
245
+ }
246
+
247
+ .worker {
248
+ margin: 0.25rem 0 0;
249
+ font-size: var(--smrt-typography-body-small-size, 0.8125rem);
250
+ color: var(--smrt-color-on-surface-variant);
251
+ }
252
+
253
+ .entry-footer {
254
+ display: flex;
255
+ justify-content: space-between;
256
+ align-items: baseline;
257
+ }
258
+
259
+ .hours {
260
+ display: flex;
261
+ align-items: baseline;
262
+ gap: 0.5rem;
263
+ }
264
+
265
+ .hours-value {
266
+ font-size: var(--smrt-typography-body-large-size, 1rem);
267
+ font-weight: var(--smrt-typography-title-medium-weight, 500);
268
+ color: var(--smrt-color-primary);
269
+ }
270
+
271
+ .rate {
272
+ font-size: var(--smrt-typography-body-small-size, 0.75rem);
273
+ color: var(--smrt-color-on-surface-variant);
274
+ }
275
+
276
+ .amount {
277
+ font-size: var(--smrt-typography-body-large-size, 1rem);
278
+ font-weight: var(--smrt-typography-title-medium-weight, 500);
279
+ color: var(--smrt-color-on-surface);
280
+ }
281
+
282
+ .mileage {
283
+ margin-top: 0.5rem;
284
+ font-size: var(--smrt-typography-body-small-size, 0.75rem);
285
+ color: var(--smrt-color-on-surface-variant);
286
+ }
287
+
288
+ .card-actions {
289
+ display: flex;
290
+ align-items: center;
291
+ padding: 0.5rem;
292
+ border-left: 1px solid var(--smrt-color-outline-variant);
293
+ }
294
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type Currency, type TimeEntry } from './utils.js';
3
+ /** Props for TimeEntryCard component */
4
+ export interface Props {
5
+ entry: TimeEntry;
6
+ href?: string;
7
+ onclick?: () => void;
8
+ selectable?: boolean;
9
+ selected?: boolean;
10
+ onselect?: (id: string, selected: boolean) => void;
11
+ currency?: Currency;
12
+ actions?: Snippet;
13
+ }
14
+ declare const TimeEntryCard: import("svelte").Component<Props, {}, "">;
15
+ type TimeEntryCard = ReturnType<typeof TimeEntryCard>;
16
+ export default TimeEntryCard;
17
+ //# sourceMappingURL=TimeEntryCard.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TimeEntryCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/TimeEntryCard.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EACL,KAAK,QAAQ,EAKb,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAGpB,wCAAwC;AACxC,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IACnD,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAkHD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}