organify-ui 0.3.49 → 0.3.51

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/index.d.ts CHANGED
@@ -1712,7 +1712,7 @@ interface InviteMemberDialogProps {
1712
1712
  email: string;
1713
1713
  }) => void;
1714
1714
  /** Default view to show when opening */
1715
- defaultView?: 'invite' | 'sent';
1715
+ defaultView?: 'invite' | 'sent' | 'received';
1716
1716
  }
1717
1717
  declare function InviteMemberDialog({ open, onOpenChange, workspaceSlug: propWorkspaceSlug, workspaceId: _deprecated, onInvited, defaultView, }: InviteMemberDialogProps): react_jsx_runtime.JSX.Element;
1718
1718
 
package/dist/index.js CHANGED
@@ -10205,13 +10205,85 @@ var GET_SENT_INVITES = `
10205
10205
  email
10206
10206
  role
10207
10207
  status
10208
- workspace {
10208
+ createdAt
10209
+ workspace {
10209
10210
  name
10210
10211
  avatarUrl
10211
10212
  }
10212
10213
  }
10213
10214
  }
10214
10215
  `;
10216
+ var GET_PENDING_INVITES = `
10217
+ query GetPendingInvites {
10218
+ pendingInvites {
10219
+ id
10220
+ workspaceId
10221
+ inviterId
10222
+ inviterName
10223
+ email
10224
+ role
10225
+ token
10226
+ status
10227
+ createdAt
10228
+ expiresAt
10229
+ workspace {
10230
+ id
10231
+ name
10232
+ slug
10233
+ avatarUrl
10234
+ }
10235
+ inviter {
10236
+ id
10237
+ name
10238
+ avatar
10239
+ }
10240
+ }
10241
+ }
10242
+ `;
10243
+ var ACCEPT_INVITE_MUTATION = `
10244
+ mutation AcceptWorkspaceInvite($token: String!) {
10245
+ acceptWorkspaceInvite(token: $token) {
10246
+ workspace {
10247
+ id
10248
+ name
10249
+ slug
10250
+ }
10251
+ member {
10252
+ id
10253
+ userId
10254
+ role
10255
+ }
10256
+ }
10257
+ }
10258
+ `;
10259
+ var DECLINE_INVITE_MUTATION = `
10260
+ mutation DeclineWorkspaceInvite($token: String!) {
10261
+ declineWorkspaceInvite(token: $token) {
10262
+ id
10263
+ status
10264
+ }
10265
+ }
10266
+ `;
10267
+ var CANCEL_INVITE_MUTATION = `
10268
+ mutation CancelWorkspaceInvite($inviteId: String!) {
10269
+ cancelWorkspaceInvite(inviteId: $inviteId) {
10270
+ cancelled
10271
+ inviteId
10272
+ }
10273
+ }
10274
+ `;
10275
+ function StatusBadge({ status }) {
10276
+ return /* @__PURE__ */ jsx(
10277
+ "span",
10278
+ {
10279
+ className: cn(
10280
+ "px-2.5 py-0.5 rounded-full text-[10px] font-semibold uppercase tracking-wider whitespace-nowrap",
10281
+ status === "PENDING" ? "bg-amber-100 text-amber-700" : status === "ACCEPTED" ? "bg-emerald-100 text-emerald-700" : "bg-slate-100 text-slate-500"
10282
+ ),
10283
+ children: status
10284
+ }
10285
+ );
10286
+ }
10215
10287
  function InviteMemberDialog({
10216
10288
  open,
10217
10289
  onOpenChange,
@@ -10228,11 +10300,15 @@ function InviteMemberDialog({
10228
10300
  const [error, setError] = React11.useState("");
10229
10301
  const [loading, setLoading] = React11.useState(false);
10230
10302
  const [sentInvites, setSentInvites] = React11.useState([]);
10231
- const [loadingInvites, setLoadingInvites] = React11.useState(false);
10303
+ const [loadingSent, setLoadingSent] = React11.useState(false);
10304
+ const [cancellingId, setCancellingId] = React11.useState(null);
10305
+ const [receivedInvites, setReceivedInvites] = React11.useState([]);
10306
+ const [loadingReceived, setLoadingReceived] = React11.useState(false);
10307
+ const [actionToken, setActionToken] = React11.useState(null);
10232
10308
  const [activeTab, setActiveTab] = React11.useState(defaultView);
10233
- const loadInvites = React11.useCallback(async () => {
10234
- if (!open || !effectiveSlug) return;
10235
- setLoadingInvites(true);
10309
+ const loadSentInvites = React11.useCallback(async () => {
10310
+ if (!effectiveSlug) return;
10311
+ setLoadingSent(true);
10236
10312
  try {
10237
10313
  const data = await gql(
10238
10314
  "workspaces",
@@ -10243,31 +10319,46 @@ function InviteMemberDialog({
10243
10319
  } catch (err) {
10244
10320
  console.error("Failed to load sent invites", err);
10245
10321
  } finally {
10246
- setLoadingInvites(false);
10322
+ setLoadingSent(false);
10247
10323
  }
10248
- }, [open, effectiveSlug, gql]);
10249
- React11.useEffect(() => {
10250
- if (open) {
10251
- setEmail("");
10252
- setRole("MEMBER");
10253
- setError("");
10254
- setActiveTab(defaultView);
10255
- loadInvites();
10324
+ }, [effectiveSlug, gql]);
10325
+ const loadReceivedInvites = React11.useCallback(async () => {
10326
+ setLoadingReceived(true);
10327
+ try {
10328
+ const data = await gql(
10329
+ "workspaces",
10330
+ GET_PENDING_INVITES,
10331
+ {}
10332
+ );
10333
+ setReceivedInvites(data?.pendingInvites || []);
10334
+ } catch (err) {
10335
+ console.error("Failed to load pending invites", err);
10336
+ } finally {
10337
+ setLoadingReceived(false);
10256
10338
  }
10257
- }, [open, loadInvites, defaultView]);
10339
+ }, [gql]);
10340
+ React11.useEffect(() => {
10341
+ if (!open) return;
10342
+ setEmail("");
10343
+ setRole("MEMBER");
10344
+ setError("");
10345
+ setActiveTab(defaultView);
10346
+ loadSentInvites();
10347
+ loadReceivedInvites();
10348
+ }, [open, defaultView, loadSentInvites, loadReceivedInvites]);
10258
10349
  const handleSubmit = async (e) => {
10259
10350
  e.preventDefault();
10260
10351
  const trimmed = email.trim().toLowerCase();
10261
10352
  if (!trimmed) {
10262
- setError("Email is required");
10353
+ setError("Email \xE9 obrigat\xF3rio");
10263
10354
  return;
10264
10355
  }
10265
10356
  if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) {
10266
- setError("Invalid email format");
10357
+ setError("Formato de email inv\xE1lido");
10267
10358
  return;
10268
10359
  }
10269
10360
  if (!effectiveSlug) {
10270
- setError("No workspace context");
10361
+ setError("Sem contexto de workspace");
10271
10362
  return;
10272
10363
  }
10273
10364
  setLoading(true);
@@ -10278,43 +10369,87 @@ function InviteMemberDialog({
10278
10369
  INVITE_MEMBER_MUTATION,
10279
10370
  { slug: effectiveSlug, input: { email: trimmed, role } }
10280
10371
  );
10281
- onOpenChange(false);
10282
10372
  onInvited?.(data.inviteWorkspaceMember);
10283
- toast.success(`Convite enviado para ${trimmed} `);
10284
- loadInvites();
10373
+ toast.success(`Convite enviado para ${trimmed}`);
10285
10374
  setEmail("");
10375
+ loadSentInvites();
10286
10376
  } catch (err) {
10287
- const msg = err.message || "Failed to send invite";
10377
+ const msg = err.message || "Falha ao enviar convite";
10288
10378
  setError(msg);
10289
10379
  toast.error(msg);
10290
10380
  } finally {
10291
10381
  setLoading(false);
10292
10382
  }
10293
10383
  };
10384
+ const handleCancelInvite = async (inviteId) => {
10385
+ setCancellingId(inviteId);
10386
+ try {
10387
+ await gql("workspaces", CANCEL_INVITE_MUTATION, { inviteId });
10388
+ toast.success("Convite cancelado");
10389
+ setSentInvites((prev) => prev.filter((i) => i.id !== inviteId));
10390
+ } catch (err) {
10391
+ toast.error(err.message || "Falha ao cancelar convite");
10392
+ } finally {
10393
+ setCancellingId(null);
10394
+ }
10395
+ };
10396
+ const handleAcceptInvite = async (token) => {
10397
+ setActionToken(token);
10398
+ try {
10399
+ const data = await gql(
10400
+ "workspaces",
10401
+ ACCEPT_INVITE_MUTATION,
10402
+ { token }
10403
+ );
10404
+ toast.success(`Entraste no workspace ${data?.acceptWorkspaceInvite?.workspace?.name ?? ""}`);
10405
+ setReceivedInvites((prev) => prev.filter((i) => i.token !== token));
10406
+ } catch (err) {
10407
+ toast.error(err.message || "Falha ao aceitar convite");
10408
+ } finally {
10409
+ setActionToken(null);
10410
+ }
10411
+ };
10412
+ const handleDeclineInvite = async (token) => {
10413
+ setActionToken(token);
10414
+ try {
10415
+ await gql("workspaces", DECLINE_INVITE_MUTATION, { token });
10416
+ toast.success("Convite recusado");
10417
+ setReceivedInvites((prev) => prev.filter((i) => i.token !== token));
10418
+ } catch (err) {
10419
+ toast.error(err.message || "Falha ao recusar convite");
10420
+ } finally {
10421
+ setActionToken(null);
10422
+ }
10423
+ };
10294
10424
  const roles = [
10295
- { value: "ADMIN", label: "Admin", desc: "Full access, manage members" },
10296
- { value: "MEMBER", label: "Member", desc: "Edit projects and tasks" },
10297
- { value: "VIEWER", label: "Viewer", desc: "Read-only access" }
10425
+ { value: "ADMIN", label: "Admin", desc: "Acesso total, gerir membros" },
10426
+ { value: "MEMBER", label: "Member", desc: "Editar projetos e tarefas" },
10427
+ { value: "VIEWER", label: "Viewer", desc: "Acesso de leitura" }
10298
10428
  ];
10429
+ const pendingReceivedCount = receivedInvites.length;
10299
10430
  return /* @__PURE__ */ jsx(
10300
10431
  ResponsiveDialog,
10301
10432
  {
10302
10433
  open,
10303
10434
  onOpenChange,
10304
- title: "Invite Member",
10305
- description: "Send an invitation to join this workspace.",
10435
+ title: "Membros & Convites",
10436
+ description: "Convide membros ou veja convites pendentes.",
10306
10437
  contentClassName: "max-w-sm",
10307
- footer: /* @__PURE__ */ jsxs("div", { className: "flex gap-2 justify-end w-full", children: [
10308
- /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), disabled: loading, children: "Cancel" }),
10438
+ footer: activeTab === "invite" ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2 justify-end w-full", children: [
10439
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), disabled: loading, children: "Cancelar" }),
10309
10440
  /* @__PURE__ */ jsxs(Button, { onClick: handleSubmit, disabled: loading || !email.trim(), children: [
10310
10441
  /* @__PURE__ */ jsx(OrgMail, { className: "w-4 h-4 mr-1.5" }),
10311
- loading ? "Sending\u2026" : "Send Invite"
10442
+ loading ? "A enviar\u2026" : "Enviar convite"
10312
10443
  ] })
10313
- ] }),
10444
+ ] }) : /* @__PURE__ */ jsx("div", { className: "flex justify-end w-full", children: /* @__PURE__ */ jsx(Button, { variant: "ghost", onClick: () => onOpenChange(false), children: "Fechar" }) }),
10314
10445
  children: /* @__PURE__ */ jsxs(Tabs, { value: activeTab, onValueChange: setActiveTab, className: "w-full", children: [
10315
- /* @__PURE__ */ jsxs(TabsList, { className: "grid grid-cols-2 mb-4", children: [
10446
+ /* @__PURE__ */ jsxs(TabsList, { className: "grid grid-cols-3 mb-4", children: [
10316
10447
  /* @__PURE__ */ jsx(TabsTrigger, { value: "invite", children: "Convidar" }),
10317
- /* @__PURE__ */ jsx(TabsTrigger, { value: "sent", children: "Enviados" })
10448
+ /* @__PURE__ */ jsx(TabsTrigger, { value: "sent", children: "Enviados" }),
10449
+ /* @__PURE__ */ jsxs(TabsTrigger, { value: "received", className: "relative", children: [
10450
+ "Recebidos",
10451
+ pendingReceivedCount > 0 && /* @__PURE__ */ jsx("span", { className: "ml-1 inline-flex items-center justify-center w-4 h-4 rounded-full bg-primary text-[9px] font-bold text-white", children: pendingReceivedCount })
10452
+ ] })
10318
10453
  ] }),
10319
10454
  /* @__PURE__ */ jsx(TabsContent, { value: "invite", children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
10320
10455
  /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
@@ -10351,7 +10486,7 @@ function InviteMemberDialog({
10351
10486
  "div",
10352
10487
  {
10353
10488
  className: cn(
10354
- "w-3 h-3 rounded-full border-2",
10489
+ "w-3 h-3 rounded-full border-2 shrink-0",
10355
10490
  role === r.value ? "bg-primary border-primary" : "border-theme-subtle-20"
10356
10491
  )
10357
10492
  }
@@ -10366,35 +10501,98 @@ function InviteMemberDialog({
10366
10501
  )) })
10367
10502
  ] })
10368
10503
  ] }) }),
10369
- /* @__PURE__ */ jsx(TabsContent, { value: "sent", children: /* @__PURE__ */ jsx("div", { className: "space-y-3 min-h-[200px]", children: loadingInvites ? /* @__PURE__ */ jsx("p", { className: "text-xs text-theme-muted text-center pt-8", children: "Carregando..." }) : sentInvites.length > 0 ? /* @__PURE__ */ jsx("div", { className: "space-y-2 max-h-60 overflow-y-auto pr-1", children: sentInvites.map((inv) => /* @__PURE__ */ jsxs(
10370
- "div",
10371
- {
10372
- className: "flex items-center justify-between p-2.5 rounded-xl border bg-theme-card text-sm border-theme-subtle",
10373
- children: [
10374
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0", children: [
10375
- /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-theme", children: inv.email }),
10376
- /* @__PURE__ */ jsxs("span", { className: "text-xs text-theme-muted", children: [
10377
- inv.role,
10378
- " ",
10379
- inv.workspace?.name ? `\u2022 ${inv.workspace.name}` : ""
10504
+ /* @__PURE__ */ jsx(TabsContent, { value: "sent", children: /* @__PURE__ */ jsx("div", { className: "space-y-3 min-h-[200px]", children: loadingSent ? /* @__PURE__ */ jsx("p", { className: "text-xs text-theme-muted text-center pt-8", children: "A carregar..." }) : sentInvites.length > 0 ? /* @__PURE__ */ jsx("div", { className: "space-y-2 max-h-72 overflow-y-auto pr-1", children: sentInvites.map((inv) => {
10505
+ const isCancelling = cancellingId === inv.id;
10506
+ return /* @__PURE__ */ jsxs(
10507
+ "div",
10508
+ {
10509
+ className: "flex items-center justify-between gap-2 p-2.5 rounded-xl border bg-theme-card text-sm border-theme-subtle",
10510
+ children: [
10511
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [
10512
+ /* @__PURE__ */ jsx("span", { className: "truncate font-medium text-theme", children: inv.email }),
10513
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-theme-muted", children: [
10514
+ inv.role,
10515
+ inv.workspace?.name ? ` \u2022 ${inv.workspace.name}` : ""
10516
+ ] })
10517
+ ] }),
10518
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
10519
+ /* @__PURE__ */ jsx(StatusBadge, { status: inv.status }),
10520
+ inv.status === "PENDING" && /* @__PURE__ */ jsx(
10521
+ "button",
10522
+ {
10523
+ type: "button",
10524
+ disabled: isCancelling || cancellingId !== null,
10525
+ onClick: () => handleCancelInvite(inv.id),
10526
+ className: "text-[10px] px-2 py-0.5 rounded-full border border-rose-300 text-rose-500 hover:bg-rose-50 disabled:opacity-40 transition-colors whitespace-nowrap",
10527
+ children: isCancelling ? "\u2026" : "Cancelar"
10528
+ }
10529
+ )
10380
10530
  ] })
10381
- ] }),
10382
- /* @__PURE__ */ jsx("div", { className: "shrink-0 ml-2", children: /* @__PURE__ */ jsx(
10383
- "span",
10384
- {
10385
- className: cn(
10386
- "px-2.5 py-0.5 rounded-full text-[10px] font-semibold uppercase tracking-wider",
10387
- inv.status === "PENDING" ? "bg-amber-100 text-amber-700" : inv.status === "ACCEPTED" ? "bg-emerald-100 text-emerald-700" : "bg-slate-100 text-slate-700 text-theme-muted"
10388
- ),
10389
- children: inv.status
10390
- }
10391
- ) })
10392
- ]
10393
- },
10394
- inv.id
10395
- )) }) : /* @__PURE__ */ jsxs("div", { className: "text-center pt-8 space-y-2", children: [
10531
+ ]
10532
+ },
10533
+ inv.id
10534
+ );
10535
+ }) }) : /* @__PURE__ */ jsxs("div", { className: "text-center pt-8 space-y-2", children: [
10396
10536
  /* @__PURE__ */ jsx("p", { className: "text-sm text-theme-muted", children: "Nenhum convite enviado ainda." }),
10397
10537
  /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: () => setActiveTab("invite"), children: "Convidar agora" })
10538
+ ] }) }) }),
10539
+ /* @__PURE__ */ jsx(TabsContent, { value: "received", children: /* @__PURE__ */ jsx("div", { className: "space-y-3 min-h-[200px]", children: loadingReceived ? /* @__PURE__ */ jsx("p", { className: "text-xs text-theme-muted text-center pt-8", children: "A carregar..." }) : receivedInvites.length > 0 ? /* @__PURE__ */ jsx("div", { className: "space-y-2 max-h-72 overflow-y-auto pr-1", children: receivedInvites.map((inv) => {
10540
+ const isActing = actionToken === inv.token;
10541
+ const anyActing = actionToken !== null;
10542
+ return /* @__PURE__ */ jsxs(
10543
+ "div",
10544
+ {
10545
+ className: "flex items-start gap-3 p-2.5 rounded-xl border bg-theme-card border-theme-subtle",
10546
+ children: [
10547
+ inv.workspace?.avatarUrl ? /* @__PURE__ */ jsx(
10548
+ "img",
10549
+ {
10550
+ src: inv.workspace.avatarUrl,
10551
+ alt: inv.workspace.name,
10552
+ className: "w-8 h-8 rounded-md object-cover shrink-0 mt-0.5"
10553
+ }
10554
+ ) : /* @__PURE__ */ jsx("div", { className: "w-8 h-8 rounded-md bg-primary/10 flex items-center justify-center shrink-0 mt-0.5", children: /* @__PURE__ */ jsx("span", { className: "text-xs font-bold text-primary", children: (inv.workspace?.name ?? "?")[0].toUpperCase() }) }),
10555
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
10556
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-theme truncate", children: inv.workspace?.name ?? "Workspace" }),
10557
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-theme-muted mt-0.5", children: [
10558
+ "Convidado como",
10559
+ " ",
10560
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-theme", children: inv.role }),
10561
+ (inv.inviter?.name || inv.inviterName) && /* @__PURE__ */ jsxs(Fragment, { children: [
10562
+ " por ",
10563
+ inv.inviter?.name ?? inv.inviterName
10564
+ ] })
10565
+ ] }),
10566
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5 mt-2", children: [
10567
+ /* @__PURE__ */ jsx(
10568
+ "button",
10569
+ {
10570
+ type: "button",
10571
+ disabled: isActing || anyActing,
10572
+ onClick: () => handleAcceptInvite(inv.token),
10573
+ className: "text-[11px] px-3 py-1 rounded-lg bg-primary text-white font-medium hover:bg-primary/90 disabled:opacity-40 transition-colors",
10574
+ children: isActing ? "A aceitar\u2026" : "Aceitar"
10575
+ }
10576
+ ),
10577
+ /* @__PURE__ */ jsx(
10578
+ "button",
10579
+ {
10580
+ type: "button",
10581
+ disabled: isActing || anyActing,
10582
+ onClick: () => handleDeclineInvite(inv.token),
10583
+ className: "text-[11px] px-3 py-1 rounded-lg border border-theme-subtle text-theme-muted hover:text-theme hover:border-theme-medium disabled:opacity-40 transition-colors",
10584
+ children: isActing ? "\u2026" : "Recusar"
10585
+ }
10586
+ )
10587
+ ] })
10588
+ ] })
10589
+ ]
10590
+ },
10591
+ inv.id
10592
+ );
10593
+ }) }) : /* @__PURE__ */ jsxs("div", { className: "text-center pt-8 space-y-1", children: [
10594
+ /* @__PURE__ */ jsx(OrgMail, { className: "w-8 h-8 text-theme-muted/40 mx-auto" }),
10595
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-theme-muted", children: "Sem convites pendentes." })
10398
10596
  ] }) }) })
10399
10597
  ] })
10400
10598
  }