@dudousxd/adonis-authkit-react 0.2.0 → 0.3.1

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/build/index.d.ts CHANGED
@@ -17,8 +17,18 @@ export { useSessions } from './src/hooks/use_sessions.js';
17
17
  export type { UseSessionsResult, AuthSession } from './src/hooks/use_sessions.js';
18
18
  export { useAuthorizedApps } from './src/hooks/use_authorized_apps.js';
19
19
  export type { UseAuthorizedAppsResult, AuthorizedApp } from './src/hooks/use_authorized_apps.js';
20
+ export { useOrganizations } from './src/hooks/use_organizations.js';
21
+ export type { UseOrganizationsResult, OrgEntry } from './src/hooks/use_organizations.js';
22
+ export { useOrganization } from './src/hooks/use_organization.js';
23
+ export type { UseOrganizationResult, ActiveOrgDetail, OrgMemberEntry } from './src/hooks/use_organization.js';
24
+ export { useSwitchOrganization } from './src/hooks/use_switch_organization.js';
25
+ export type { UseSwitchOrganizationResult } from './src/hooks/use_switch_organization.js';
26
+ export { useOrgInvitations } from './src/hooks/use_org_invitations.js';
27
+ export type { UseOrgInvitationsResult, OrgInvitationEntry } from './src/hooks/use_org_invitations.js';
20
28
  export { jsonRequest, useResource } from './src/hooks/use_resource.js';
21
29
  export type { ResourceState } from './src/hooks/use_resource.js';
30
+ export { usePasswordStrength, heuristicScorer, } from './src/hooks/use_password_strength.js';
31
+ export type { PasswordStrengthScore, PasswordStrengthResult, PasswordScorer, UsePasswordStrengthOptions, } from './src/hooks/use_password_strength.js';
22
32
  export { deriveInitials, currentUrl } from './src/utils.js';
23
33
  export { Authenticated, Guest } from './src/components/authenticated.js';
24
34
  export type { AuthenticatedProps, GuestProps } from './src/components/authenticated.js';
@@ -36,5 +46,11 @@ export { UserProfile } from './src/components/user_profile.js';
36
46
  export type { UserProfileProps } from './src/components/user_profile.js';
37
47
  export { AuthorizedApps } from './src/components/authorized_apps.js';
38
48
  export type { AuthorizedAppsProps } from './src/components/authorized_apps.js';
49
+ export { PasswordStrengthMeter } from './src/components/password_strength_meter.js';
50
+ export type { PasswordStrengthMeterProps } from './src/components/password_strength_meter.js';
51
+ export { OrganizationSwitcher } from './src/components/organization_switcher.js';
52
+ export type { OrganizationSwitcherProps } from './src/components/organization_switcher.js';
53
+ export { OrganizationProfile } from './src/components/organization_profile.js';
54
+ export type { OrganizationProfileProps } from './src/components/organization_profile.js';
39
55
  export { hasGlobalRole, hasAnyGlobalRole, hasAllGlobalRoles, hasAppRole, hasAnyAppRole, hasAllAppRoles, } from './src/roles.js';
40
56
  export type { AuthUser, AuthSharedProps, AuthState } from './src/types.js';
package/build/index.js CHANGED
@@ -8,7 +8,12 @@ export { useUser } from './src/hooks/use_user.js';
8
8
  export { useProfile } from './src/hooks/use_profile.js';
9
9
  export { useSessions } from './src/hooks/use_sessions.js';
10
10
  export { useAuthorizedApps } from './src/hooks/use_authorized_apps.js';
11
+ export { useOrganizations } from './src/hooks/use_organizations.js';
12
+ export { useOrganization } from './src/hooks/use_organization.js';
13
+ export { useSwitchOrganization } from './src/hooks/use_switch_organization.js';
14
+ export { useOrgInvitations } from './src/hooks/use_org_invitations.js';
11
15
  export { jsonRequest, useResource } from './src/hooks/use_resource.js';
16
+ export { usePasswordStrength, heuristicScorer, } from './src/hooks/use_password_strength.js';
12
17
  export { deriveInitials, currentUrl } from './src/utils.js';
13
18
  export { Authenticated, Guest } from './src/components/authenticated.js';
14
19
  export { Can } from './src/components/can.js';
@@ -18,4 +23,7 @@ export { Avatar } from './src/components/avatar.js';
18
23
  export { UserButton } from './src/components/user_button.js';
19
24
  export { UserProfile } from './src/components/user_profile.js';
20
25
  export { AuthorizedApps } from './src/components/authorized_apps.js';
26
+ export { PasswordStrengthMeter } from './src/components/password_strength_meter.js';
27
+ export { OrganizationSwitcher } from './src/components/organization_switcher.js';
28
+ export { OrganizationProfile } from './src/components/organization_profile.js';
21
29
  export { hasGlobalRole, hasAnyGlobalRole, hasAllGlobalRoles, hasAppRole, hasAnyAppRole, hasAllAppRoles, } from './src/roles.js';
@@ -0,0 +1,8 @@
1
+ export interface OrganizationProfileProps {
2
+ inviteLabel?: string;
3
+ leaveLabel?: string;
4
+ className?: string;
5
+ }
6
+ export declare function OrganizationProfile({ inviteLabel, leaveLabel, className, }: OrganizationProfileProps): import("react").DetailedReactHTMLElement<{
7
+ className: string;
8
+ }, HTMLElement> | null;
@@ -0,0 +1,87 @@
1
+ import { createElement, useState } from 'react';
2
+ import { useOrganizations } from '../hooks/use_organizations.js';
3
+ import { useOrganization } from '../hooks/use_organization.js';
4
+ import { useAuth } from '../use_auth.js';
5
+ import { useAuthkitConfig } from '../config.js';
6
+ import { jsonRequest } from '../hooks/use_resource.js';
7
+ export function OrganizationProfile({ inviteLabel = 'Convidar membro', leaveLabel = 'Sair da organização', className, }) {
8
+ const { isAuthenticated } = useAuth();
9
+ const config = useAuthkitConfig();
10
+ const { activeOrgId, actions: orgListActions } = useOrganizations();
11
+ const { data: org, loading, error, actions } = useOrganization(activeOrgId);
12
+ const [inviteEmail, setInviteEmail] = useState('');
13
+ const [inviteRole, setInviteRole] = useState('member');
14
+ const [inviteLoading, setInviteLoading] = useState(false);
15
+ const [inviteError, setInviteError] = useState(null);
16
+ if (!isAuthenticated || !activeOrgId)
17
+ return null;
18
+ if (loading)
19
+ return createElement('div', { className: 'authkit-org-profile__loading' }, 'Carregando…');
20
+ if (error)
21
+ return createElement('div', { className: 'authkit-error' }, error.message);
22
+ if (!org)
23
+ return null;
24
+ const base = config.endpoints.orgs.replace('/json', '');
25
+ const handleInvite = async (e) => {
26
+ e.preventDefault();
27
+ if (!inviteEmail.trim())
28
+ return;
29
+ setInviteLoading(true);
30
+ setInviteError(null);
31
+ try {
32
+ await jsonRequest(`${base}/${encodeURIComponent(activeOrgId)}/invite`, {
33
+ method: 'POST',
34
+ body: JSON.stringify({ email: inviteEmail.trim(), role: inviteRole }),
35
+ csrfToken: config.csrfToken,
36
+ });
37
+ setInviteEmail('');
38
+ await actions.refetch();
39
+ }
40
+ catch (err) {
41
+ setInviteError(err);
42
+ }
43
+ finally {
44
+ setInviteLoading(false);
45
+ }
46
+ };
47
+ const handleLeave = async () => {
48
+ try {
49
+ await jsonRequest(`${base}/${encodeURIComponent(activeOrgId)}/leave`, {
50
+ method: 'POST',
51
+ csrfToken: config.csrfToken,
52
+ });
53
+ await orgListActions.refetch();
54
+ }
55
+ catch {
56
+ }
57
+ };
58
+ const memberList = createElement('div', { className: 'authkit-org-profile__members' }, createElement('h3', { className: 'authkit-org-profile__section-title' }, 'Membros'), ...(org.members.length === 0
59
+ ? [createElement('p', { className: 'authkit-org-profile__empty' }, 'Nenhum membro.')]
60
+ : org.members.map((m) => createElement('div', { key: m.accountId, className: 'authkit-org-profile__member' }, createElement('div', { className: 'authkit-org-profile__member-info' }, createElement('span', { className: 'authkit-org-profile__member-email' }, m.email ?? m.accountId), createElement('span', { className: 'authkit-org-profile__member-role' }, m.role))))));
61
+ const inviteForm = org.canManage
62
+ ? createElement('form', { className: 'authkit-org-profile__invite-form', onSubmit: handleInvite }, createElement('h3', { className: 'authkit-org-profile__section-title' }, inviteLabel), createElement('input', {
63
+ className: 'authkit-input',
64
+ type: 'email',
65
+ placeholder: 'Email',
66
+ value: inviteEmail,
67
+ onChange: (e) => setInviteEmail(e.target.value),
68
+ }), createElement('input', {
69
+ className: 'authkit-input',
70
+ placeholder: 'Papel (ex: member)',
71
+ value: inviteRole,
72
+ onChange: (e) => setInviteRole(e.target.value),
73
+ }), inviteError
74
+ ? createElement('p', { className: 'authkit-error', role: 'alert' }, inviteError.message)
75
+ : null, createElement('button', {
76
+ type: 'submit',
77
+ className: 'authkit-button authkit-button--primary',
78
+ disabled: inviteLoading,
79
+ }, inviteLoading ? 'Enviando…' : inviteLabel))
80
+ : null;
81
+ const leaveButton = createElement('button', {
82
+ type: 'button',
83
+ className: 'authkit-button authkit-button--danger',
84
+ onClick: handleLeave,
85
+ }, leaveLabel);
86
+ return createElement('div', { className: ['authkit-card', 'authkit-org-profile', className].filter(Boolean).join(' ') }, createElement('div', { className: 'authkit-org-profile__header' }, createElement('div', { className: 'authkit-org-profile__name' }, org.name), createElement('div', { className: 'authkit-org-profile__slug' }, org.slug)), memberList, inviteForm, leaveButton);
87
+ }
@@ -0,0 +1,7 @@
1
+ export interface OrganizationSwitcherProps {
2
+ personalAccountLabel?: string;
3
+ className?: string;
4
+ }
5
+ export declare function OrganizationSwitcher({ personalAccountLabel, className, }: OrganizationSwitcherProps): import("react").DetailedReactHTMLElement<{
6
+ className: string;
7
+ }, HTMLElement> | null;
@@ -0,0 +1,57 @@
1
+ import { createElement, useState } from 'react';
2
+ import { useOrganizations } from '../hooks/use_organizations.js';
3
+ import { useSwitchOrganization } from '../hooks/use_switch_organization.js';
4
+ import { useAuth } from '../use_auth.js';
5
+ export function OrganizationSwitcher({ personalAccountLabel = 'Conta pessoal', className, }) {
6
+ const { isAuthenticated } = useAuth();
7
+ const { data: orgs, activeOrgId, supported } = useOrganizations();
8
+ const { activate, deactivate, loading: switching } = useSwitchOrganization();
9
+ const [open, setOpen] = useState(false);
10
+ if (!isAuthenticated || !supported)
11
+ return null;
12
+ const activeOrg = orgs?.find((o) => o.id === activeOrgId) ?? null;
13
+ const label = activeOrg ? activeOrg.name : personalAccountLabel;
14
+ const trigger = createElement('button', {
15
+ type: 'button',
16
+ className: 'authkit-orgswitcher__trigger',
17
+ 'aria-haspopup': 'listbox',
18
+ 'aria-expanded': open,
19
+ disabled: switching,
20
+ onClick: () => setOpen((v) => !v),
21
+ }, createElement('span', { className: 'authkit-orgswitcher__label' }, label), createElement('span', { className: 'authkit-orgswitcher__chevron', 'aria-hidden': 'true' }, '▾'));
22
+ const menu = open
23
+ ? createElement('div', { className: 'authkit-orgswitcher__menu', role: 'listbox' }, createElement('button', {
24
+ type: 'button',
25
+ className: [
26
+ 'authkit-orgswitcher__item',
27
+ !activeOrgId ? 'authkit-orgswitcher__item--active' : '',
28
+ ]
29
+ .filter(Boolean)
30
+ .join(' '),
31
+ role: 'option',
32
+ 'aria-selected': !activeOrgId,
33
+ onClick: async () => {
34
+ setOpen(false);
35
+ if (activeOrgId)
36
+ await deactivate();
37
+ },
38
+ }, createElement('span', { className: 'authkit-orgswitcher__item-name' }, personalAccountLabel)), ...(orgs ?? []).map((org) => createElement('button', {
39
+ key: org.id,
40
+ type: 'button',
41
+ className: [
42
+ 'authkit-orgswitcher__item',
43
+ org.isActive ? 'authkit-orgswitcher__item--active' : '',
44
+ ]
45
+ .filter(Boolean)
46
+ .join(' '),
47
+ role: 'option',
48
+ 'aria-selected': org.isActive,
49
+ onClick: async () => {
50
+ setOpen(false);
51
+ if (!org.isActive)
52
+ await activate(org.id);
53
+ },
54
+ }, createElement('span', { className: 'authkit-orgswitcher__item-name' }, org.name), createElement('span', { className: 'authkit-orgswitcher__item-role' }, org.role))))
55
+ : null;
56
+ return createElement('div', { className: ['authkit-orgswitcher', className].filter(Boolean).join(' ') }, trigger, menu);
57
+ }
@@ -0,0 +1,11 @@
1
+ import { type PasswordScorer } from '../hooks/use_password_strength.js';
2
+ export interface PasswordStrengthMeterProps {
3
+ password: string;
4
+ scorer?: PasswordScorer;
5
+ showFeedback?: boolean;
6
+ labels?: [string, string, string, string, string];
7
+ className?: string;
8
+ }
9
+ export declare function PasswordStrengthMeter({ password, scorer, showFeedback, labels, className, }: PasswordStrengthMeterProps): import("react").DetailedReactHTMLElement<{
10
+ className: string;
11
+ }, HTMLElement>;
@@ -0,0 +1,39 @@
1
+ import { createElement } from 'react';
2
+ import { usePasswordStrength, } from '../hooks/use_password_strength.js';
3
+ const DEFAULT_LABELS = [
4
+ 'Very weak',
5
+ 'Weak',
6
+ 'Fair',
7
+ 'Good',
8
+ 'Strong',
9
+ ];
10
+ export function PasswordStrengthMeter({ password, scorer, showFeedback = true, labels = DEFAULT_LABELS, className, }) {
11
+ const { score, feedback } = usePasswordStrength(password, { scorer });
12
+ const cls = ['authkit-strength', className].filter(Boolean).join(' ');
13
+ const segments = [0, 1, 2, 3].map((i) => createElement('span', {
14
+ key: i,
15
+ className: [
16
+ 'authkit-strength__segment',
17
+ i < score ? 'authkit-strength__segment--filled' : '',
18
+ ]
19
+ .filter(Boolean)
20
+ .join(' '),
21
+ }));
22
+ const children = [
23
+ createElement('div', {
24
+ key: 'bar',
25
+ className: 'authkit-strength__bar',
26
+ role: 'meter',
27
+ 'aria-valuemin': 0,
28
+ 'aria-valuemax': 4,
29
+ 'aria-valuenow': score,
30
+ 'aria-label': labels[score],
31
+ 'data-score': score,
32
+ }, segments),
33
+ createElement('span', { key: 'label', className: 'authkit-strength__label' }, labels[score]),
34
+ ];
35
+ if (showFeedback && feedback && feedback.length > 0) {
36
+ children.push(createElement('ul', { key: 'feedback', className: 'authkit-strength__feedback' }, feedback.map((tip, i) => createElement('li', { key: i }, tip))));
37
+ }
38
+ return createElement('div', { className: cls }, children);
39
+ }
@@ -3,6 +3,8 @@ export interface AuthkitEndpoints {
3
3
  sessions: string;
4
4
  apps: string;
5
5
  passkeys: string;
6
+ orgs: string;
7
+ orgInvitations: string;
6
8
  }
7
9
  export interface AuthkitConfig {
8
10
  loginUrl?: string;
@@ -8,6 +8,8 @@ export const DEFAULT_CONFIG = {
8
8
  sessions: '/account/security',
9
9
  apps: '/account/apps',
10
10
  passkeys: '/account/mfa/passkeys',
11
+ orgs: '/account/orgs/json',
12
+ orgInvitations: '/account/orgs/invitations/json',
11
13
  },
12
14
  };
13
15
  export function resolveConfig(config) {
@@ -20,6 +22,8 @@ export function resolveConfig(config) {
20
22
  sessions: config?.endpoints?.sessions ?? DEFAULT_CONFIG.endpoints.sessions,
21
23
  apps: config?.endpoints?.apps ?? DEFAULT_CONFIG.endpoints.apps,
22
24
  passkeys: config?.endpoints?.passkeys ?? DEFAULT_CONFIG.endpoints.passkeys,
25
+ orgs: config?.endpoints?.orgs ?? DEFAULT_CONFIG.endpoints.orgs,
26
+ orgInvitations: config?.endpoints?.orgInvitations ?? DEFAULT_CONFIG.endpoints.orgInvitations,
23
27
  },
24
28
  csrfToken: config?.csrfToken,
25
29
  };
@@ -0,0 +1,19 @@
1
+ import { type ResourceState } from './use_resource.js';
2
+ export interface OrgInvitationEntry {
3
+ id: string;
4
+ organizationId: string;
5
+ orgName: string;
6
+ orgSlug: string;
7
+ email: string;
8
+ role: string;
9
+ expiresAt: string;
10
+ createdAt: string;
11
+ [key: string]: unknown;
12
+ }
13
+ export interface UseOrgInvitationsResult extends ResourceState<OrgInvitationEntry[]> {
14
+ actions: {
15
+ refetch(): Promise<void>;
16
+ accept(token: string): Promise<void>;
17
+ };
18
+ }
19
+ export declare function useOrgInvitations(): UseOrgInvitationsResult;
@@ -0,0 +1,20 @@
1
+ import { useCallback } from 'react';
2
+ import { useAuthkitConfig } from '../config.js';
3
+ import { jsonRequest, useResource } from './use_resource.js';
4
+ export function useOrgInvitations() {
5
+ const config = useAuthkitConfig();
6
+ const { data, loading, error, refetch } = useResource(config.endpoints.orgInvitations, config.csrfToken);
7
+ const accept = useCallback(async (token) => {
8
+ await jsonRequest(`/account/orgs/invitations/${encodeURIComponent(token)}/accept`, {
9
+ method: 'POST',
10
+ csrfToken: config.csrfToken,
11
+ });
12
+ await refetch();
13
+ }, [config.csrfToken, refetch]);
14
+ return {
15
+ data: data?.invitations ?? null,
16
+ loading,
17
+ error,
18
+ actions: { refetch, accept },
19
+ };
20
+ }
@@ -0,0 +1,23 @@
1
+ import { type ResourceState } from './use_resource.js';
2
+ export interface OrgMemberEntry {
3
+ accountId: string;
4
+ email: string | null;
5
+ role: string;
6
+ joinedAt: string;
7
+ }
8
+ export interface ActiveOrgDetail {
9
+ id: string;
10
+ name: string;
11
+ slug: string;
12
+ logoUrl: string | null;
13
+ role: string;
14
+ canManage: boolean;
15
+ members: OrgMemberEntry[];
16
+ [key: string]: unknown;
17
+ }
18
+ export interface UseOrganizationResult extends ResourceState<ActiveOrgDetail> {
19
+ actions: {
20
+ refetch(): Promise<void>;
21
+ };
22
+ }
23
+ export declare function useOrganization(orgId: string | null): UseOrganizationResult;
@@ -0,0 +1,8 @@
1
+ import { useAuthkitConfig } from '../config.js';
2
+ import { useResource } from './use_resource.js';
3
+ export function useOrganization(orgId) {
4
+ const config = useAuthkitConfig();
5
+ const url = orgId ? `${config.endpoints.orgs.replace('/json', '')}/${encodeURIComponent(orgId)}/json` : '';
6
+ const { data, loading, error, refetch } = useResource(url, config.csrfToken);
7
+ return { data, loading: url ? loading : false, error, actions: { refetch } };
8
+ }
@@ -0,0 +1,18 @@
1
+ import { type ResourceState } from './use_resource.js';
2
+ export interface OrgEntry {
3
+ id: string;
4
+ name: string;
5
+ slug: string;
6
+ logoUrl: string | null;
7
+ role: string;
8
+ isActive: boolean;
9
+ [key: string]: unknown;
10
+ }
11
+ export interface UseOrganizationsResult extends ResourceState<OrgEntry[]> {
12
+ activeOrgId: string | null;
13
+ supported: boolean;
14
+ actions: {
15
+ refetch(): Promise<void>;
16
+ };
17
+ }
18
+ export declare function useOrganizations(): UseOrganizationsResult;
@@ -0,0 +1,14 @@
1
+ import { useAuthkitConfig } from '../config.js';
2
+ import { useResource } from './use_resource.js';
3
+ export function useOrganizations() {
4
+ const config = useAuthkitConfig();
5
+ const { data, loading, error, refetch } = useResource(config.endpoints.orgs, config.csrfToken);
6
+ return {
7
+ data: data?.orgs ?? null,
8
+ loading,
9
+ error,
10
+ activeOrgId: data?.activeOrgId ?? null,
11
+ supported: data?.supported ?? true,
12
+ actions: { refetch },
13
+ };
14
+ }
@@ -0,0 +1,11 @@
1
+ export type PasswordStrengthScore = 0 | 1 | 2 | 3 | 4;
2
+ export interface PasswordStrengthResult {
3
+ score: PasswordStrengthScore;
4
+ feedback?: string[];
5
+ }
6
+ export type PasswordScorer = (password: string) => PasswordStrengthResult;
7
+ export interface UsePasswordStrengthOptions {
8
+ scorer?: PasswordScorer;
9
+ }
10
+ export declare function heuristicScorer(password: string): PasswordStrengthResult;
11
+ export declare function usePasswordStrength(password: string, options?: UsePasswordStrengthOptions): PasswordStrengthResult;
@@ -0,0 +1,34 @@
1
+ import { useMemo } from 'react';
2
+ export function heuristicScorer(password) {
3
+ if (!password)
4
+ return { score: 0, feedback: ['Enter a password.'] };
5
+ const feedback = [];
6
+ const classes = [/[a-z]/, /[A-Z]/, /[0-9]/, /[^A-Za-z0-9]/];
7
+ const variety = classes.filter((re) => re.test(password)).length;
8
+ const length = password.length;
9
+ let points = 0;
10
+ if (length >= 8)
11
+ points += 1;
12
+ if (length >= 12)
13
+ points += 1;
14
+ if (length >= 16)
15
+ points += 1;
16
+ if (variety >= 2)
17
+ points += 1;
18
+ if (variety >= 3)
19
+ points += 1;
20
+ if (length < 12)
21
+ feedback.push('Use at least 12 characters.');
22
+ if (!/[A-Z]/.test(password))
23
+ feedback.push('Add an uppercase letter.');
24
+ if (!/[0-9]/.test(password))
25
+ feedback.push('Add a number.');
26
+ if (!/[^A-Za-z0-9]/.test(password))
27
+ feedback.push('Add a symbol.');
28
+ const score = Math.max(0, Math.min(4, points));
29
+ return { score, feedback: feedback.length ? feedback : undefined };
30
+ }
31
+ export function usePasswordStrength(password, options = {}) {
32
+ const scorer = options.scorer ?? heuristicScorer;
33
+ return useMemo(() => scorer(password), [password, scorer]);
34
+ }
@@ -0,0 +1,7 @@
1
+ export interface UseSwitchOrganizationResult {
2
+ loading: boolean;
3
+ error: Error | null;
4
+ activate(orgId: string): Promise<void>;
5
+ deactivate(): Promise<void>;
6
+ }
7
+ export declare function useSwitchOrganization(): UseSwitchOrganizationResult;
@@ -0,0 +1,42 @@
1
+ import { useCallback, useState } from 'react';
2
+ import { useAuthkitConfig } from '../config.js';
3
+ import { jsonRequest } from './use_resource.js';
4
+ export function useSwitchOrganization() {
5
+ const config = useAuthkitConfig();
6
+ const base = config.endpoints.orgs.replace('/json', '');
7
+ const [loading, setLoading] = useState(false);
8
+ const [error, setError] = useState(null);
9
+ const activate = useCallback(async (orgId) => {
10
+ setLoading(true);
11
+ setError(null);
12
+ try {
13
+ await jsonRequest(`${base}/${encodeURIComponent(orgId)}/activate`, {
14
+ method: 'POST',
15
+ csrfToken: config.csrfToken,
16
+ });
17
+ }
18
+ catch (err) {
19
+ setError(err);
20
+ }
21
+ finally {
22
+ setLoading(false);
23
+ }
24
+ }, [base, config.csrfToken]);
25
+ const deactivate = useCallback(async () => {
26
+ setLoading(true);
27
+ setError(null);
28
+ try {
29
+ await jsonRequest(`${base}/deactivate`, {
30
+ method: 'POST',
31
+ csrfToken: config.csrfToken,
32
+ });
33
+ }
34
+ catch (err) {
35
+ setError(err);
36
+ }
37
+ finally {
38
+ setLoading(false);
39
+ }
40
+ }, [base, config.csrfToken]);
41
+ return { loading, error, activate, deactivate };
42
+ }
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@dudousxd/adonis-authkit-react",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Frontend ergonomics over AuthKit for AdonisJS + Inertia + React apps: a typed useAuth() hook, role-gating hooks and gating components.",
5
5
  "license": "MIT",
6
6
  "author": "dudousxd",
7
- "homepage": "https://github.com/DavideCarvalho/streaming-educacao/tree/main/packages/authkit-react#readme",
7
+ "homepage": "https://github.com/DavideCarvalho/adonis-authkit/tree/main/packages/authkit-react#readme",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/DavideCarvalho/streaming-educacao.git",
10
+ "url": "https://github.com/DavideCarvalho/adonis-authkit.git",
11
11
  "directory": "packages/authkit-react"
12
12
  },
13
13
  "bugs": {
14
- "url": "https://github.com/DavideCarvalho/streaming-educacao/issues"
14
+ "url": "https://github.com/DavideCarvalho/adonis-authkit/issues"
15
15
  },
16
16
  "keywords": [
17
17
  "adonisjs",
@@ -43,7 +43,7 @@
43
43
  "react-dom": "19.2.6"
44
44
  },
45
45
  "dependencies": {
46
- "@dudousxd/adonis-authkit-core": "0.2.0"
46
+ "@dudousxd/adonis-authkit-core": "0.3.1"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@adonisjs/tsconfig": "2.0.0",
package/styles.css CHANGED
@@ -187,3 +187,108 @@
187
187
  color: var(--authkit-muted);
188
188
  font-size: 0.9rem;
189
189
  }
190
+
191
+ /* Medidor de força de senha (PasswordStrengthMeter). */
192
+ .authkit-strength {
193
+ display: flex;
194
+ flex-direction: column;
195
+ gap: 0.35rem;
196
+ }
197
+ .authkit-strength__bar {
198
+ display: flex;
199
+ gap: 0.25rem;
200
+ }
201
+ .authkit-strength__segment {
202
+ flex: 1 1 0;
203
+ height: 5px;
204
+ border-radius: 999px;
205
+ background: var(--authkit-border);
206
+ transition: background-color 0.15s ease;
207
+ }
208
+ /* Cor por score, do mais fraco ao mais forte (via data-score no bar). */
209
+ .authkit-strength__bar[data-score='1'] .authkit-strength__segment--filled {
210
+ background: var(--authkit-danger);
211
+ }
212
+ .authkit-strength__bar[data-score='2'] .authkit-strength__segment--filled {
213
+ background: #f5a524;
214
+ }
215
+ .authkit-strength__bar[data-score='3'] .authkit-strength__segment--filled {
216
+ background: #d6c20a;
217
+ }
218
+ .authkit-strength__bar[data-score='4'] .authkit-strength__segment--filled {
219
+ background: #2fb344;
220
+ }
221
+ .authkit-strength__label {
222
+ font-size: 0.8rem;
223
+ color: var(--authkit-muted);
224
+ }
225
+ .authkit-strength__feedback {
226
+ margin: 0;
227
+ padding-left: 1.1rem;
228
+ font-size: 0.8rem;
229
+ color: var(--authkit-muted);
230
+ }
231
+
232
+ /* ─── OrganizationSwitcher ─────────────────────────────────────────────── */
233
+ .authkit-orgswitcher {
234
+ position: relative;
235
+ display: inline-block;
236
+ }
237
+ .authkit-orgswitcher__trigger {
238
+ display: inline-flex;
239
+ align-items: center;
240
+ gap: 0.4rem;
241
+ padding: 0.4rem 0.75rem;
242
+ background: var(--authkit-bg);
243
+ border: 1px solid var(--authkit-border);
244
+ border-radius: var(--authkit-radius);
245
+ cursor: pointer;
246
+ font-size: 0.875rem;
247
+ color: var(--authkit-fg);
248
+ }
249
+ .authkit-orgswitcher__trigger:disabled { opacity: 0.6; cursor: default; }
250
+ .authkit-orgswitcher__chevron { font-size: 0.65rem; color: var(--authkit-muted); }
251
+ .authkit-orgswitcher__menu {
252
+ position: absolute;
253
+ top: calc(100% + 4px);
254
+ left: 0;
255
+ min-width: 180px;
256
+ background: var(--authkit-bg);
257
+ border: 1px solid var(--authkit-border);
258
+ border-radius: var(--authkit-radius);
259
+ box-shadow: 0 4px 16px rgba(0,0,0,.1);
260
+ z-index: 50;
261
+ overflow: hidden;
262
+ }
263
+ .authkit-orgswitcher__item {
264
+ display: flex;
265
+ align-items: center;
266
+ justify-content: space-between;
267
+ width: 100%;
268
+ padding: 0.5rem 1rem;
269
+ background: transparent;
270
+ border: none;
271
+ cursor: pointer;
272
+ font-size: 0.875rem;
273
+ color: var(--authkit-fg);
274
+ text-align: left;
275
+ }
276
+ .authkit-orgswitcher__item:hover { background: color-mix(in srgb, var(--authkit-primary) 8%, transparent); }
277
+ .authkit-orgswitcher__item--active { font-weight: 600; }
278
+ .authkit-orgswitcher__item-name { flex: 1; }
279
+ .authkit-orgswitcher__item-role { font-size: 0.75rem; color: var(--authkit-muted); margin-left: 0.5rem; }
280
+
281
+ /* ─── OrganizationProfile ──────────────────────────────────────────────── */
282
+ .authkit-org-profile { display: flex; flex-direction: column; gap: var(--authkit-gap); }
283
+ .authkit-org-profile__header { margin-bottom: 0.5rem; }
284
+ .authkit-org-profile__name { font-weight: 600; font-size: 1rem; color: var(--authkit-fg); }
285
+ .authkit-org-profile__slug { font-size: 0.8rem; color: var(--authkit-muted); }
286
+ .authkit-org-profile__section-title { font-size: 0.85rem; font-weight: 600; color: var(--authkit-fg); margin-bottom: 0.4rem; }
287
+ .authkit-org-profile__members { display: flex; flex-direction: column; gap: 0.25rem; }
288
+ .authkit-org-profile__member { display: flex; align-items: center; gap: 0.5rem; padding: 0.3rem 0; border-bottom: 1px solid var(--authkit-border); }
289
+ .authkit-org-profile__member:last-child { border-bottom: none; }
290
+ .authkit-org-profile__member-info { display: flex; flex-direction: column; }
291
+ .authkit-org-profile__member-email { font-size: 0.875rem; color: var(--authkit-fg); }
292
+ .authkit-org-profile__member-role { font-size: 0.75rem; color: var(--authkit-muted); }
293
+ .authkit-org-profile__invite-form { display: flex; flex-direction: column; gap: 0.4rem; margin-top: 0.75rem; }
294
+ .authkit-org-profile__empty { font-size: 0.875rem; color: var(--authkit-muted); }