diff --git a/src/app/project/index.ts b/src/app/project/index.ts index 95763e5..3bb23db 100644 --- a/src/app/project/index.ts +++ b/src/app/project/index.ts @@ -15,3 +15,5 @@ export { AccessSettings } from "./settings/AccessSettings"; export { LabelsSettings } from "./settings/LabelsSettings"; export { BillingSettings } from "./settings/BillingSettings"; export { ProjectCreateMenuModal } from "./components/ProjectCreateMenuModal"; +export { ProjectJoinPage } from "./ProjectJoinPage"; +export { ProjectInvitationPage } from "./ProjectInvitationPage"; diff --git a/src/app/project/layout.tsx b/src/app/project/layout.tsx index 1327d46..f384db1 100644 --- a/src/app/project/layout.tsx +++ b/src/app/project/layout.tsx @@ -1,19 +1,24 @@ -import { Outlet, useMatch, useParams } from "react-router-dom"; -import { useState } from "react"; -import { PanelLeftOpen } from "lucide-react"; +import { Link, Outlet, useMatch, useParams } from "react-router-dom"; +import { createContext, useContext, useState } from "react"; +import { Lock, PanelLeftOpen } from "lucide-react"; import { ServerIconRail } from "@/components/layout/ServerIconRail"; import { ChannelSidebar } from "@/components/layout/ChannelSidebar"; import { Header } from "@/components/layout/Header"; import { MemberList } from "@/components/layout/MemberList"; import { RoomProvider } from "@/contexts/room"; -import { createContext, useContext } from "react"; import { useIsMobile, useIsTablet } from "@/hooks/use-mobile"; +import { useProjectInfo } from "@/hooks/useProjectInfo"; +import type { ProjectInfoRelational } from "@/client/model"; +import { Button } from "@/components/ui/button"; interface ProjectContextType { showMembers: boolean; setShowMembers: (v: boolean) => void; currentRoomName: string | null; setCurrentRoomName: (name: string | null) => void; + projectInfo: ProjectInfoRelational | null; + isProjectMember: boolean; + isProjectPreview: boolean; } const ProjectContext = createContext({ @@ -21,29 +26,78 @@ const ProjectContext = createContext({ setShowMembers: () => {}, currentRoomName: null, setCurrentRoomName: () => {}, + projectInfo: null, + isProjectMember: false, + isProjectPreview: false, }); // eslint-disable-next-line react-refresh/only-export-components export const useProjectLayout = () => useContext(ProjectContext); +export function ProjectJoinBanner({ + compact = false, + message = "Join this project to participate and use project tools.", +}: { + compact?: boolean; + message?: string; +}) { + const { projectInfo } = useProjectLayout(); + const projectName = projectInfo?.name; + + return ( +
+
+
+ +
+
+

Preview mode

+

{message}

+
+
+ {projectName && ( + + )} +
+ ); +} + export function ProjectLayout() { const [showMembers, setShowMembers] = useState(false); const [currentRoomName, setCurrentRoomName] = useState(null); const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); const { projectName } = useParams<{ projectName: string }>(); + const { data: projectInfo = null } = useProjectInfo(projectName); const channelMatch = useMatch("/:projectName/channel/:roomId"); const chatMatch = useMatch("/:projectName/chat/*"); const roomId = channelMatch?.params.roomId ?? null; const isMobile = useIsMobile(); const isTablet = useIsTablet(); - const canShowMembers = !isMobile && !isTablet; + const isProjectMember = !!projectInfo?.role; + const isProjectPreview = !!projectInfo && !projectInfo.role; + const canShowMembers = !isMobile && !isTablet && isProjectMember; const mainShouldOwnScroll = !channelMatch && !chatMatch; return ( - - + +
{!isMobile && } @@ -89,6 +143,16 @@ export function ProjectLayout() { style={{ backgroundColor: "var(--surface-ground)" }} >
+ {isProjectPreview && ( + + )}