@dilipod/ui 0.4.5 → 0.4.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dilipod/ui",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "Dilipod Design System - Shared UI components and styles",
5
5
  "author": "Dilipod <hello@dilipod.com>",
6
6
  "license": "MIT",
@@ -1,9 +1,8 @@
1
1
  'use client'
2
2
 
3
3
  import { useState } from 'react'
4
- import { Card, CardContent, CardHeader, CardTitle } from './card'
4
+ import { Card, CardContent } from './card'
5
5
  import { Button } from './button'
6
- import { IconBox } from './icon-box'
7
6
  import { toast } from './use-toast'
8
7
  import { cn } from '../lib/utils'
9
8
 
@@ -152,18 +151,11 @@ export function ImpactMetricsForm({
152
151
  const netAnnualSavings = laborSavingsPerYear - workerCostPerYear
153
152
 
154
153
  return (
155
- <Card className={cn("border-[var(--cyan)]/20 bg-gradient-to-br from-white to-[var(--cyan)]/5", className)}>
156
- <CardHeader className="pb-3">
157
- <div className="flex items-center justify-between">
158
- <CardTitle className="flex items-center gap-2">
159
- <IconBox size="sm">
160
- {/* ChartLineUp icon */}
161
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" viewBox="0 0 256 256">
162
- <path d="M232,208a8,8,0,0,1-8,8H32a8,8,0,0,1-8-8V48a8,8,0,0,1,16,0V156.69l50.34-50.35a8,8,0,0,1,11.32,0L128,132.69l58.34-58.35a8,8,0,0,1,11.32,11.32l-64,64a8,8,0,0,1-11.32,0L96,123.31,40,179.31V200H224A8,8,0,0,1,232,208Z"/>
163
- </svg>
164
- </IconBox>
165
- Impact Metrics (ROI)
166
- </CardTitle>
154
+ <Card className={cn("", className)}>
155
+ <CardContent className="p-5">
156
+ {/* Header */}
157
+ <div className="flex items-center justify-between mb-4">
158
+ <p className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Impact Metrics (ROI)</p>
167
159
  <div className="flex items-center gap-2">
168
160
  {isEditing ? (
169
161
  <>
@@ -195,140 +187,103 @@ export function ImpactMetricsForm({
195
187
  )}
196
188
  </div>
197
189
  </div>
198
- </CardHeader>
199
- <CardContent>
200
- <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-4">
190
+
191
+ {/* Metrics Grid */}
192
+ <div className="grid grid-cols-2 lg:grid-cols-4 gap-6">
201
193
  {/* Time per task */}
202
- <div className="flex items-start gap-3">
203
- <div className="p-2 rounded-sm bg-[var(--cyan)]/10 shrink-0">
204
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 256 256" className="text-[var(--cyan)]">
205
- <path d="M128,40a96,96,0,1,0,96,96A96.11,96.11,0,0,0,128,40Zm0,176a80,80,0,1,1,80-80A80.09,80.09,0,0,1,128,216ZM173.66,90.34a8,8,0,0,1,0,11.32l-40,40a8,8,0,0,1-11.32-11.32l40-40A8,8,0,0,1,173.66,90.34ZM96,16a8,8,0,0,1,8-8h48a8,8,0,0,1,0,16H104A8,8,0,0,1,96,16Z"/>
206
- </svg>
207
- </div>
208
- <div className="flex-1">
209
- <label className="text-sm text-muted-foreground block mb-1">Time per Task</label>
210
- {isEditing ? (
211
- <div className="flex items-center gap-2">
212
- <input
213
- type="number"
214
- value={metrics.time_saved_minutes_per_run}
215
- onChange={(e) => setMetrics(prev => ({
216
- ...prev,
217
- time_saved_minutes_per_run: parseInt(e.target.value) || 0
218
- }))}
219
- className="w-16 px-2 py-1 text-lg font-bold border border-border rounded-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan)] bg-background"
220
- min="0"
221
- />
222
- <span className="text-muted-foreground">min</span>
223
- </div>
224
- ) : (
225
- <p className="text-2xl font-bold">
226
- {metrics.time_saved_minutes_per_run} <span className="text-base font-normal text-muted-foreground">min</span>
227
- </p>
228
- )}
229
- <p className="text-xs text-muted-foreground mt-1">How long manually</p>
230
- </div>
194
+ <div>
195
+ <p className="text-xs text-muted-foreground uppercase tracking-wide mb-1">Time per Task</p>
196
+ {isEditing ? (
197
+ <div className="flex items-baseline gap-1">
198
+ <input
199
+ type="number"
200
+ value={metrics.time_saved_minutes_per_run}
201
+ onChange={(e) => setMetrics(prev => ({
202
+ ...prev,
203
+ time_saved_minutes_per_run: parseInt(e.target.value) || 0
204
+ }))}
205
+ className="w-16 px-2 py-1 text-2xl font-bold border border-border rounded-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan)] bg-background"
206
+ min="0"
207
+ />
208
+ <span className="text-sm text-muted-foreground">min</span>
209
+ </div>
210
+ ) : (
211
+ <p className="text-2xl font-bold">
212
+ {metrics.time_saved_minutes_per_run}<span className="text-sm font-normal text-muted-foreground ml-1">min</span>
213
+ </p>
214
+ )}
231
215
  </div>
232
216
 
233
217
  {/* Manual cost */}
234
- <div className="flex items-start gap-3">
235
- <div className="p-2 rounded-sm bg-[var(--cyan)]/10 shrink-0">
236
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 256 256" className="text-[var(--cyan)]">
237
- <path d="M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm0-144a8,8,0,0,1,8,8v4.4c14.25,3.14,24,14.43,24,30.6,0,4.42-3.58,8-8,8s-8-3.58-8-8c0-8.64-7.18-13-16-13s-16,4.36-16,13,7.18,13,16,13c17.64,0,32,11.35,32,29,0,16.17-9.75,27.46-24,30.6V192a8,8,0,0,1-16,0v-4.4c-14.25-3.14-24-14.43-24-30.6a8,8,0,0,1,16,0c0,8.64,7.18,13,16,13s16-4.36,16-13-7.18-13-16-13c-17.64,0-32-11.35-32-29,0-16.17,9.75-27.46,24-30.6V80A8,8,0,0,1,128,72Z"/>
238
- </svg>
239
- </div>
240
- <div className="flex-1">
241
- <label className="text-sm text-muted-foreground block mb-1">Manual Cost</label>
242
- {isEditing ? (
243
- <div className="flex items-center gap-2">
244
- <span className="text-muted-foreground">€</span>
245
- <input
246
- type="number"
247
- value={metrics.hourly_rate_euros}
248
- onChange={(e) => setMetrics(prev => ({
249
- ...prev,
250
- hourly_rate_euros: parseFloat(e.target.value) || 0
251
- }))}
252
- className="w-16 px-2 py-1 text-lg font-bold border border-border rounded-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan)] bg-background"
253
- min="0"
254
- step="0.5"
255
- />
256
- <span className="text-muted-foreground">/hr</span>
257
- </div>
258
- ) : (
259
- <p className="text-2xl font-bold">
260
- €{metrics.hourly_rate_euros} <span className="text-base font-normal text-muted-foreground">/hr</span>
261
- </p>
262
- )}
263
- <p className="text-xs text-muted-foreground mt-1">Employee hourly cost</p>
264
- </div>
218
+ <div>
219
+ <p className="text-xs text-muted-foreground uppercase tracking-wide mb-1">Manual Cost</p>
220
+ {isEditing ? (
221
+ <div className="flex items-baseline gap-1">
222
+ <span className="text-sm text-muted-foreground">€</span>
223
+ <input
224
+ type="number"
225
+ value={metrics.hourly_rate_euros}
226
+ onChange={(e) => setMetrics(prev => ({
227
+ ...prev,
228
+ hourly_rate_euros: parseFloat(e.target.value) || 0
229
+ }))}
230
+ className="w-16 px-2 py-1 text-2xl font-bold border border-border rounded-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan)] bg-background"
231
+ min="0"
232
+ step="0.5"
233
+ />
234
+ <span className="text-sm text-muted-foreground">/hr</span>
235
+ </div>
236
+ ) : (
237
+ <p className="text-2xl font-bold">
238
+ €{metrics.hourly_rate_euros}<span className="text-sm font-normal text-muted-foreground ml-1">/hr</span>
239
+ </p>
240
+ )}
265
241
  </div>
266
242
 
267
243
  {/* Job portion */}
268
- <div className="flex items-start gap-3">
269
- <div className="p-2 rounded-sm bg-[var(--cyan)]/10 shrink-0">
270
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 256 256" className="text-[var(--cyan)]">
271
- <path d="M230.92,212c-15.23-26.33-38.7-45.21-66.09-54.16a72,72,0,1,0-73.66,0C63.78,166.78,40.31,185.66,25.08,212a8,8,0,1,0,13.85,8c18.84-32.56,52.14-52,89.07-52s70.23,19.44,89.07,52a8,8,0,1,0,13.85-8ZM72,96a56,56,0,1,1,56,56A56.06,56.06,0,0,1,72,96Z"/>
272
- </svg>
273
- </div>
274
- <div className="flex-1">
275
- <label className="text-sm text-muted-foreground block mb-1">Job Portion</label>
276
- {isEditing ? (
277
- <div className="flex items-center gap-2">
278
- <input
279
- type="number"
280
- value={Math.round(metrics.fte_equivalent * 100)}
281
- onChange={(e) => setMetrics(prev => ({
282
- ...prev,
283
- fte_equivalent: (parseFloat(e.target.value) || 0) / 100
284
- }))}
285
- className="w-16 px-2 py-1 text-lg font-bold border border-border rounded-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan)] bg-background"
286
- min="0"
287
- max="1000"
288
- step="5"
289
- />
290
- <span className="text-muted-foreground">%</span>
291
- </div>
292
- ) : (
293
- <p className="text-2xl font-bold">
294
- {Math.round(metrics.fte_equivalent * 100)} <span className="text-base font-normal text-muted-foreground">%</span>
295
- </p>
296
- )}
297
- <p className="text-xs text-muted-foreground mt-1">% of FTE ({hoursSavedPerYear}h/year)</p>
298
- </div>
244
+ <div>
245
+ <p className="text-xs text-muted-foreground uppercase tracking-wide mb-1">Job Portion</p>
246
+ {isEditing ? (
247
+ <div className="flex items-baseline gap-1">
248
+ <input
249
+ type="number"
250
+ value={Math.round(metrics.fte_equivalent * 100)}
251
+ onChange={(e) => setMetrics(prev => ({
252
+ ...prev,
253
+ fte_equivalent: (parseFloat(e.target.value) || 0) / 100
254
+ }))}
255
+ className="w-16 px-2 py-1 text-2xl font-bold border border-border rounded-sm focus:outline-none focus:ring-2 focus:ring-[var(--cyan)] bg-background"
256
+ min="0"
257
+ max="1000"
258
+ step="5"
259
+ />
260
+ <span className="text-sm text-muted-foreground">%</span>
261
+ </div>
262
+ ) : (
263
+ <p className="text-2xl font-bold">
264
+ {Math.round(metrics.fte_equivalent * 100)}<span className="text-sm font-normal text-muted-foreground ml-1">%</span>
265
+ </p>
266
+ )}
267
+ <p className="text-xs text-muted-foreground mt-0.5">{hoursSavedPerYear}h/year</p>
299
268
  </div>
300
269
 
301
270
  {/* Net Annual Savings */}
302
- <div className="flex items-start gap-3">
303
- <div className="p-2 rounded-sm bg-[var(--cyan)]/10 shrink-0">
304
- {/* TrendUp icon */}
305
- <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 256 256" className="text-[var(--cyan)]">
306
- <path d="M240,56v64a8,8,0,0,1-16,0V75.31l-82.34,82.35a8,8,0,0,1-11.32,0L96,123.31,29.66,189.66a8,8,0,0,1-11.32-11.32l72-72a8,8,0,0,1,11.32,0L136,140.69,212.69,64H168a8,8,0,0,1,0-16h64A8,8,0,0,1,240,56Z"/>
307
- </svg>
308
- </div>
309
- <div className="flex-1">
310
- <label className="text-sm text-muted-foreground block mb-1">Net Annual Savings</label>
311
- <p className={cn("text-2xl font-bold", netAnnualSavings >= 0 ? "text-[var(--cyan)]" : "text-red-500")}>
312
- €{netAnnualSavings.toLocaleString(undefined, { maximumFractionDigits: 0 })}
313
- </p>
314
- <p className="text-xs text-muted-foreground mt-1">
315
- €{laborSavingsPerYear.toLocaleString(undefined, { maximumFractionDigits: 0 })} labor − €{workerCostPerYear} worker
316
- </p>
317
- </div>
271
+ <div>
272
+ <p className="text-xs text-muted-foreground uppercase tracking-wide mb-1">Net Annual Savings</p>
273
+ <p className={cn("text-2xl font-bold", netAnnualSavings >= 0 ? "text-[var(--cyan)]" : "text-red-500")}>
274
+ €{netAnnualSavings.toLocaleString(undefined, { maximumFractionDigits: 0 })}
275
+ </p>
276
+ <p className="text-xs text-muted-foreground mt-0.5">
277
+ €{laborSavingsPerYear.toLocaleString(undefined, { maximumFractionDigits: 0 })} − €{workerCostPerYear}
278
+ </p>
318
279
  </div>
319
280
  </div>
320
281
 
321
282
  {/* Implied frequency indicator */}
322
283
  {impliedFrequencyPerYear > 0 && (
323
- <div className="mt-4 pt-4 border-t border-border/50">
324
- <p className="text-sm text-muted-foreground">
325
- <span className="font-medium">Implied frequency:</span>{' '}
326
- ~{impliedFrequencyPerMonth}/month ({impliedFrequencyPerYear}/year)
327
- <span className="text-xs ml-2">
328
- based on {metrics.time_saved_minutes_per_run} min/task × {Math.round(metrics.fte_equivalent * 100)}% FTE
329
- </span>
330
- </p>
331
- </div>
284
+ <p className="text-xs text-muted-foreground mt-4 pt-3 border-t border-border/50">
285
+ Implied: ~{impliedFrequencyPerMonth}×/month ({impliedFrequencyPerYear}×/year)
286
+ </p>
332
287
  )}
333
288
  </CardContent>
334
289
  </Card>