@greatapps/common 1.1.90 → 1.1.92

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