@tuturuuu/ui 0.16.0 → 0.17.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.
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
4
- import { BarChart3, Flag, Sparkles, Target, Trophy } from '@tuturuuu/icons';
4
+ import { BarChart3, Flag, Target, Trophy, Upload } from '@tuturuuu/icons';
5
5
  import {
6
6
  createTaskLeaderboard,
7
7
  createTaskLeaderboardTeam,
@@ -40,7 +40,6 @@ export type TaskProgressView =
40
40
  | 'import';
41
41
 
42
42
  interface TaskProgressPageProps {
43
- onViewChange?: (view: TaskProgressView) => void;
44
43
  routeWsId: string;
45
44
  view: TaskProgressView;
46
45
  wsId: string;
@@ -88,7 +87,6 @@ function parseImportRows(text: string, metrics: TaskProgressMetric[]) {
88
87
  }
89
88
 
90
89
  export function TaskProgressPage({
91
- onViewChange,
92
90
  routeWsId,
93
91
  view,
94
92
  wsId,
@@ -244,32 +242,34 @@ export function TaskProgressPage({
244
242
  const leaderboards = leaderboardsQuery.data?.ok
245
243
  ? leaderboardsQuery.data.leaderboards
246
244
  : [];
245
+ const tabs = [
246
+ { icon: Flag, value: 'progress' },
247
+ { icon: Target, value: 'goals' },
248
+ { icon: BarChart3, value: 'stats' },
249
+ { icon: Trophy, value: 'leaderboards' },
250
+ { icon: Upload, value: 'import' },
251
+ ] as const;
247
252
 
248
253
  return (
249
254
  <div className="mx-auto flex w-full max-w-[1600px] flex-col gap-6 p-4 md:p-6 lg:p-8">
250
- <section className="relative overflow-hidden rounded-3xl border bg-gradient-to-br from-background via-background to-dynamic-blue/10 p-5 shadow-sm md:p-7">
251
- <div className="pointer-events-none absolute -top-24 -right-20 h-64 w-64 rounded-full bg-dynamic-blue/10 blur-3xl" />
252
- <div className="relative flex flex-col gap-5 xl:flex-row xl:items-end xl:justify-between">
253
- <div className="max-w-2xl space-y-3">
254
- <div className="flex items-center gap-2 font-semibold text-dynamic-blue text-xs uppercase tracking-[0.18em]">
255
- <Sparkles className="h-4 w-4" />
255
+ <section className="overflow-hidden rounded-2xl border bg-card/60 shadow-sm backdrop-blur">
256
+ <div className="flex flex-col gap-4 p-5 md:flex-row md:items-center md:justify-between md:p-6">
257
+ <div className="max-w-2xl">
258
+ <h1 className="font-bold text-2xl tracking-tight md:text-3xl">
256
259
  {t('views.progress.title')}
257
- </div>
258
- <div>
259
- <h1 className="font-bold text-3xl tracking-tight md:text-4xl">
260
- {t(`views.${view}.title`)}
261
- </h1>
262
- <p className="mt-2 text-muted-foreground">
263
- {t(`views.${view}.description`)}
264
- </p>
265
- </div>
260
+ </h1>
261
+ <p className="mt-1 text-muted-foreground text-sm md:text-base">
262
+ {t(`views.${view}.description`)}
263
+ </p>
264
+ </div>
265
+ <div className="shrink-0">
266
266
  {metrics.length > 0 ? (
267
- <label className="inline-flex items-center gap-3 rounded-xl border bg-background/80 px-3 py-2 text-sm shadow-sm backdrop-blur">
267
+ <label className="flex min-w-56 items-center justify-between gap-3 rounded-xl border bg-background px-3 py-2 text-sm shadow-sm">
268
268
  <span className="text-muted-foreground">
269
269
  {t('fields.metric_name')}
270
270
  </span>
271
271
  <select
272
- className="min-w-32 bg-transparent font-medium outline-none"
272
+ className="min-w-28 bg-transparent text-right font-medium outline-none"
273
273
  onChange={(event) => setSelectedMetricId(event.target.value)}
274
274
  value={selectedMetric?.id ?? ''}
275
275
  >
@@ -282,41 +282,29 @@ export function TaskProgressPage({
282
282
  </label>
283
283
  ) : null}
284
284
  </div>
285
- <nav className="flex max-w-full gap-1 overflow-x-auto rounded-2xl border bg-background/80 p-1.5 shadow-sm backdrop-blur">
286
- {(
287
- ['progress', 'goals', 'stats', 'leaderboards', 'import'] as const
288
- ).map((tab) =>
289
- onViewChange ? (
290
- <Button
291
- key={tab}
292
- onClick={() => onViewChange(tab)}
293
- size="sm"
294
- type="button"
295
- variant={tab === view ? 'default' : 'ghost'}
296
- >
297
- {t(`tabs.${tab}`)}
298
- </Button>
299
- ) : (
300
- <Button
301
- key={tab}
302
- asChild
303
- size="sm"
304
- variant={tab === view ? 'default' : 'ghost'}
305
- >
306
- <Link
307
- href={
308
- tab === 'progress'
309
- ? `/${routeWsId}/progress`
310
- : `/${routeWsId}/progress/${tab}`
311
- }
312
- >
313
- {t(`tabs.${tab}`)}
314
- </Link>
315
- </Button>
316
- )
317
- )}
318
- </nav>
319
285
  </div>
286
+ <nav className="flex max-w-full gap-1 overflow-x-auto border-t bg-muted/20 p-2">
287
+ {tabs.map(({ icon: TabIcon, value: tab }) => (
288
+ <Button
289
+ key={tab}
290
+ asChild
291
+ className="shrink-0 gap-2 rounded-lg"
292
+ size="sm"
293
+ variant={tab === view ? 'secondary' : 'ghost'}
294
+ >
295
+ <Link
296
+ href={
297
+ tab === 'progress'
298
+ ? `/${routeWsId}/progress`
299
+ : `/${routeWsId}/progress/${tab}`
300
+ }
301
+ >
302
+ <TabIcon className="size-4" />
303
+ {t(`tabs.${tab}`)}
304
+ </Link>
305
+ </Button>
306
+ ))}
307
+ </nav>
320
308
  </section>
321
309
 
322
310
  {hasPendingSchema ? (
package/src/globals.css CHANGED
@@ -1,4 +1,5 @@
1
1
  @import "tailwindcss";
2
+ @import "slot-text/style.css";
2
3
  @source "components";
3
4
  @source "../../../node_modules/streamdown/dist/*.js";
4
5
  @source "../../../node_modules/@streamdown/math/dist/*.js";