@greatapps/common 1.1.90 → 1.1.91
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/components/navigation/AppNavigation.mjs +26 -0
- package/dist/components/navigation/AppNavigation.mjs.map +1 -0
- package/dist/components/navigation/AppNavigationContext.mjs +13 -0
- package/dist/components/navigation/AppNavigationContext.mjs.map +1 -0
- package/dist/components/navigation/NavItem.mjs +25 -0
- package/dist/components/navigation/NavItem.mjs.map +1 -0
- package/dist/components/navigation/ProjectSelectorPopover.mjs +136 -0
- package/dist/components/navigation/ProjectSelectorPopover.mjs.map +1 -0
- package/dist/components/navigation/shells/DesktopShell.mjs +11 -0
- package/dist/components/navigation/shells/DesktopShell.mjs.map +1 -0
- package/dist/components/navigation/shells/MdShell.mjs +11 -0
- package/dist/components/navigation/shells/MdShell.mjs.map +1 -0
- package/dist/components/navigation/shells/MobileShell.mjs +27 -0
- package/dist/components/navigation/shells/MobileShell.mjs.map +1 -0
- package/dist/components/navigation/subcomponents/CreditsCard.mjs +30 -0
- package/dist/components/navigation/subcomponents/CreditsCard.mjs.map +1 -0
- package/dist/components/navigation/subcomponents/NavItems.mjs +19 -0
- package/dist/components/navigation/subcomponents/NavItems.mjs.map +1 -0
- package/dist/components/navigation/subcomponents/PlanCard.mjs +25 -0
- package/dist/components/navigation/subcomponents/PlanCard.mjs.map +1 -0
- package/dist/components/navigation/subcomponents/ProjectSelector.mjs +122 -0
- package/dist/components/navigation/subcomponents/ProjectSelector.mjs.map +1 -0
- package/dist/components/navigation/types.mjs +1 -0
- package/dist/components/navigation/types.mjs.map +1 -0
- package/dist/index.mjs +4 -0
- package/dist/index.mjs.map +1 -1
- package/dist/infra/api/client.mjs +8 -6
- package/dist/infra/api/client.mjs.map +1 -1
- package/dist/store/useMobileNavbarSheet.mjs +11 -0
- package/dist/store/useMobileNavbarSheet.mjs.map +1 -0
- package/package.json +1 -1
- package/src/components/navigation/AppNavigation.tsx +36 -0
- package/src/components/navigation/AppNavigationContext.ts +17 -0
- package/src/components/navigation/NavItem.tsx +31 -0
- package/src/components/navigation/ProjectSelectorPopover.tsx +152 -0
- package/src/components/navigation/shells/DesktopShell.tsx +15 -0
- package/src/components/navigation/shells/MdShell.tsx +15 -0
- package/src/components/navigation/shells/MobileShell.tsx +29 -0
- package/src/components/navigation/subcomponents/CreditsCard.tsx +30 -0
- package/src/components/navigation/subcomponents/NavItems.tsx +31 -0
- package/src/components/navigation/subcomponents/PlanCard.tsx +29 -0
- package/src/components/navigation/subcomponents/ProjectSelector.tsx +146 -0
- package/src/components/navigation/types.ts +6 -0
- package/src/index.ts +6 -0
- package/src/infra/api/client.ts +3 -7
- package/src/store/useMobileNavbarSheet.ts +15 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useRef, RefObject } from 'react';
|
|
4
|
+
import Image from 'next/image';
|
|
5
|
+
import { useAppNavigationContext } from '../AppNavigationContext';
|
|
6
|
+
import { ProjectSelectorPopover } from '../ProjectSelectorPopover';
|
|
7
|
+
import { UsersSelectorPopover } from '../../layouts/UsersSelectorPopover';
|
|
8
|
+
import { Button, Command, CommandEmpty, CommandInput, CommandItem, CommandList } from '../../../index';
|
|
9
|
+
import { ChevronDown, Plus, Settings } from 'lucide-react';
|
|
10
|
+
import type { NavigationProject } from '../types';
|
|
11
|
+
|
|
12
|
+
export interface ProjectSelectorProps {
|
|
13
|
+
projects: NavigationProject[];
|
|
14
|
+
selectedProject: NavigationProject | null;
|
|
15
|
+
isLoading?: boolean;
|
|
16
|
+
onSelectProject: (project: NavigationProject) => void;
|
|
17
|
+
onAddProject: () => void;
|
|
18
|
+
onManageProjects: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function ProjectItemSkeleton() {
|
|
22
|
+
return (
|
|
23
|
+
<div className="flex items-center gap-2 p-2">
|
|
24
|
+
<div className="size-8 rounded-lg bg-gray-100 animate-pulse shrink-0" />
|
|
25
|
+
<div className="h-3.5 w-32 rounded bg-gray-100 animate-pulse" />
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function ProjectSelector({
|
|
31
|
+
projects,
|
|
32
|
+
selectedProject,
|
|
33
|
+
isLoading,
|
|
34
|
+
onSelectProject,
|
|
35
|
+
onAddProject,
|
|
36
|
+
onManageProjects,
|
|
37
|
+
}: ProjectSelectorProps) {
|
|
38
|
+
const { breakpoint } = useAppNavigationContext();
|
|
39
|
+
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
40
|
+
const [activeSection, setActiveSection] = useState<'projects' | null>(null);
|
|
41
|
+
const titleRef = useRef<HTMLDivElement>(null);
|
|
42
|
+
|
|
43
|
+
// Desktop and md: title + popover + users selector (same visual as current gpages sidebars)
|
|
44
|
+
if (breakpoint === 'desktop' || breakpoint === 'md') {
|
|
45
|
+
return (
|
|
46
|
+
<div className="flex items-center justify-between">
|
|
47
|
+
<div className="flex items-center gap-2 truncate select-none" ref={titleRef}>
|
|
48
|
+
{isLoading ? (
|
|
49
|
+
<div className="h-4 w-32 rounded bg-gray-100 animate-pulse" />
|
|
50
|
+
) : (
|
|
51
|
+
<span
|
|
52
|
+
className="cursor-pointer paragraph-medium-semibold text-gray-600 truncate"
|
|
53
|
+
onClick={() => setIsPopoverOpen((v) => !v)}
|
|
54
|
+
>
|
|
55
|
+
{selectedProject?.title && selectedProject.title.length > 15
|
|
56
|
+
? `${selectedProject.title.slice(0, 15)}…`
|
|
57
|
+
: selectedProject?.title}
|
|
58
|
+
</span>
|
|
59
|
+
)}
|
|
60
|
+
<ProjectSelectorPopover
|
|
61
|
+
open={isPopoverOpen}
|
|
62
|
+
setOpen={setIsPopoverOpen}
|
|
63
|
+
titleRef={titleRef as unknown as RefObject<HTMLSpanElement | null>}
|
|
64
|
+
projects={projects}
|
|
65
|
+
selectedProject={selectedProject}
|
|
66
|
+
onSelectProject={onSelectProject}
|
|
67
|
+
onAddProject={onAddProject}
|
|
68
|
+
onManageProjects={onManageProjects}
|
|
69
|
+
/>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="flex items-center gap-2">
|
|
72
|
+
<UsersSelectorPopover projectId={selectedProject?.id} />
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Mobile: inline collapsible accordion
|
|
79
|
+
return (
|
|
80
|
+
<div className="flex flex-col gap-2.5">
|
|
81
|
+
<div className="flex items-center justify-between">
|
|
82
|
+
<div className="flex items-center gap-1">
|
|
83
|
+
<span className="paragraph-medium-semibold text-gray-600">{selectedProject?.title}</span>
|
|
84
|
+
<ChevronDown
|
|
85
|
+
size={20}
|
|
86
|
+
onClick={() => setActiveSection(activeSection === 'projects' ? null : 'projects')}
|
|
87
|
+
className={`transition-transform duration-200 cursor-pointer ${
|
|
88
|
+
activeSection === 'projects' ? 'rotate-180 text-gray-950' : 'text-gray-400'
|
|
89
|
+
}`}
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
<UsersSelectorPopover projectId={selectedProject?.id} />
|
|
93
|
+
</div>
|
|
94
|
+
{activeSection === 'projects' && (
|
|
95
|
+
<div className="flex flex-col p-3 gap-2 border border-gray-200 rounded-lg animate-in fade-in duration-400">
|
|
96
|
+
<div className="flex items-center justify-between">
|
|
97
|
+
<span className="paragraph-small-semibold">Projetos</span>
|
|
98
|
+
<div className="flex items-center gap-1.5">
|
|
99
|
+
<Button variant="secondary" className="h-8! paragraph-small-semibold text-gray-950" onClick={onAddProject}>
|
|
100
|
+
<Plus size={16} />
|
|
101
|
+
Adicionar
|
|
102
|
+
</Button>
|
|
103
|
+
<Button variant="secondary" className="p-0 size-8" onClick={onManageProjects}>
|
|
104
|
+
<Settings size={18} />
|
|
105
|
+
</Button>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
<Command className="gap-2">
|
|
109
|
+
<CommandInput placeholder="Busque aqui" />
|
|
110
|
+
<CommandList className="custom-scrollbar paragraph-small-medium text-gray-600 h-[162px]">
|
|
111
|
+
<CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
|
|
112
|
+
{isLoading ? (
|
|
113
|
+
<>
|
|
114
|
+
<ProjectItemSkeleton />
|
|
115
|
+
<ProjectItemSkeleton />
|
|
116
|
+
</>
|
|
117
|
+
) : (
|
|
118
|
+
projects.map((project) => (
|
|
119
|
+
<CommandItem
|
|
120
|
+
key={project.id}
|
|
121
|
+
value={project.title}
|
|
122
|
+
className={selectedProject?.id === project.id ? 'bg-gray-50' : ''}
|
|
123
|
+
onSelect={() => { onSelectProject(project); setActiveSection(null); }}
|
|
124
|
+
>
|
|
125
|
+
<Image
|
|
126
|
+
alt="Icon Project"
|
|
127
|
+
src={
|
|
128
|
+
project.photo
|
|
129
|
+
? `${process.env.NEXT_PUBLIC_STORAGE_URL}/${project.photo}`
|
|
130
|
+
: '/images/Logomark.png'
|
|
131
|
+
}
|
|
132
|
+
width={32}
|
|
133
|
+
height={32}
|
|
134
|
+
className="rounded-lg"
|
|
135
|
+
/>
|
|
136
|
+
{project.title}
|
|
137
|
+
</CommandItem>
|
|
138
|
+
))
|
|
139
|
+
)}
|
|
140
|
+
</CommandList>
|
|
141
|
+
</Command>
|
|
142
|
+
</div>
|
|
143
|
+
)}
|
|
144
|
+
</div>
|
|
145
|
+
);
|
|
146
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -105,6 +105,12 @@ export type {
|
|
|
105
105
|
export { NavBarItem } from "./components/layouts/NavBarItem";
|
|
106
106
|
export type { NavBarItemProps } from "./components/layouts/NavBarItem";
|
|
107
107
|
|
|
108
|
+
// Navigation
|
|
109
|
+
export { AppNavigation } from './components/navigation/AppNavigation';
|
|
110
|
+
export type { NavItemConfig } from './components/navigation/subcomponents/NavItems';
|
|
111
|
+
export type { NavigationProject } from './components/navigation/types';
|
|
112
|
+
export { useMobileNavbarSheet } from './store/useMobileNavbarSheet';
|
|
113
|
+
|
|
108
114
|
// UI - Buttons
|
|
109
115
|
export { Button, buttonVariants } from "./components/ui/buttons/Button";
|
|
110
116
|
export { CopyButton } from "./components/ui/buttons/CopyButton";
|
package/src/infra/api/client.ts
CHANGED
|
@@ -117,19 +117,15 @@ class ApiClient {
|
|
|
117
117
|
return this.request<T>(endpoint, {
|
|
118
118
|
...config,
|
|
119
119
|
method: 'POST',
|
|
120
|
-
body: data ? JSON.stringify(data) : undefined,
|
|
120
|
+
body: data instanceof FormData ? data : data ? JSON.stringify(data) : undefined,
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
async postFormData<T>(endpoint: string, data: FormData, config?: RequestConfig): Promise<T> {
|
|
125
|
-
return this.request<T>(endpoint, { ...config, method: 'POST', body: data });
|
|
126
|
-
}
|
|
127
|
-
|
|
128
124
|
async put<T>(endpoint: string, data?: unknown, config?: RequestConfig): Promise<T> {
|
|
129
125
|
return this.request<T>(endpoint, {
|
|
130
126
|
...config,
|
|
131
127
|
method: 'PUT',
|
|
132
|
-
body: data ? JSON.stringify(data) : undefined,
|
|
128
|
+
body: data instanceof FormData ? data : data ? JSON.stringify(data) : undefined,
|
|
133
129
|
});
|
|
134
130
|
}
|
|
135
131
|
|
|
@@ -137,7 +133,7 @@ class ApiClient {
|
|
|
137
133
|
return this.request<T>(endpoint, {
|
|
138
134
|
...config,
|
|
139
135
|
method: 'PATCH',
|
|
140
|
-
body: data ? JSON.stringify(data) : undefined,
|
|
136
|
+
body: data instanceof FormData ? data : data ? JSON.stringify(data) : undefined,
|
|
141
137
|
});
|
|
142
138
|
}
|
|
143
139
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
|
|
3
|
+
interface MobileNavbarSheetStore {
|
|
4
|
+
isOpen: boolean;
|
|
5
|
+
open: () => void;
|
|
6
|
+
close: () => void;
|
|
7
|
+
toggle: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const useMobileNavbarSheet = create<MobileNavbarSheetStore>((set) => ({
|
|
11
|
+
isOpen: false,
|
|
12
|
+
open: () => set({ isOpen: true }),
|
|
13
|
+
close: () => set({ isOpen: false }),
|
|
14
|
+
toggle: () => set((state) => ({ isOpen: !state.isOpen })),
|
|
15
|
+
}));
|