first commit

This commit is contained in:
2025-06-10 15:28:23 +05:30
commit e22a2dc275
699 changed files with 100382 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
import React, { useEffect, useState } from "react";
import RenderOverlay from "./Overlay";
import { ArrowIcon, CloseIcon } from "../icons/ExportCommonIcons";
import { AccessOption, User } from "../../types/users";
import RegularDropDown from "../ui/inputs/RegularDropDown";
import { access } from "fs";
import MultiEmailInvite from "../ui/inputs/MultiEmailInvite";
import { useActiveUsers } from "../../store/builder/store";
interface UserListTemplateProps {
user: User;
}
const UserListTemplate: React.FC<UserListTemplateProps> = ({ user }) => {
const [accessSelection, setAccessSelection] = useState<string>(user.access);
function accessUpdate({ option }: AccessOption) {
setAccessSelection(option);
}
return (
<div className="user-list-container">
<div className="user-details">
<div className="profile-image">
{user.profileImage ? (
<img
src={user.profileImage || "https://via.placeholder.com/150"}
alt={`${user.name}'s profile`}
/>
) : (
<div
className="no-profile-container"
style={{ background: user.color }}
>
{user.name[0]}
</div>
)}
</div>
<div className="user-name">{user.name}</div>
</div>
<div className="user-access">
<RegularDropDown
header={accessSelection}
options={["Admin", "User", "Viewer"]}
onSelect={(option) => accessUpdate({ option })}
search={false}
/>
{/* <ArrowIcon /> */}
</div>
</div>
);
};
interface CollaborateProps {
setUserManagement: (value: boolean) => void;
}
const CollaborationPopup: React.FC<CollaborateProps> = ({
setUserManagement,
}) => {
const { activeUsers } = useActiveUsers();
useEffect(() => {
console.log("activeUsers: ", activeUsers);
}, [activeUsers]);
const userName = localStorage.getItem("userName") || "Anonymous";
const users = [
{
name: "Alice Johnson",
email: "alice.johnson@example.com",
profileImage: "",
color: "#FF6600",
access: "Admin",
},
{
name: "Bob Smith",
email: "bob.smith@example.com",
profileImage: "",
color: "#488EF6",
access: "Viewer",
},
{
name: "Charlie Brown",
email: "charlie.brown@example.com",
profileImage: "",
color: "#48AC2A",
access: "Viewer",
},
{
name: "Diana Prince",
email: "diana.prince@example.com",
profileImage: "",
color: "#D44242",
access: "Viewer",
},
];
return (
<RenderOverlay>
<div
className="collaboration-popup-wrapper"
onClick={() => {
setUserManagement(false);
}}
>
<div
className="collaboration-popup-container"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
}}
>
<div className="header">
<div className="content">Share this file</div>
<div className="content">
<div className="copy-link-button">copy link</div>
<div
className="close-button"
onClick={() => {
setUserManagement(false);
}}
>
<CloseIcon />
</div>
</div>
</div>
<div className="invite-input-container">
<MultiEmailInvite />
</div>
<div className="split"></div>
<section>
<div className="access-and-user-control-container">
<div className="user-header">Who has access</div>
<div className="general-access-container">
<div className="team-container">
<div className="project-name">Untitled</div>
<div className="number-of-peoples-have-access">
{users.length} persons
</div>
</div>
</div>
<div className="users-list-container">
<div className="you-container">
<div className="your-name">
<div className="user-profile">{userName[0].toUpperCase()}</div>
{userName}
</div>
<div className="indicater">you</div>
</div>
{users.map((user, index) => (
<UserListTemplate key={index} user={user} />
))}
</div>
</div>
</section>
</div>
</div>
</RenderOverlay>
);
};
export default CollaborationPopup;

View File

@@ -0,0 +1,29 @@
import React from "react";
import RenderOverlay from "./Overlay";
import { useSelectedUserStore } from "../../store/collaboration/useCollabStore";
import { useCamMode } from "../../store/builder/store";
const FollowPerson: React.FC = () => {
// Get the selected user from the store
const { selectedUser, clearSelectedUser } = useSelectedUserStore();
const { setCamMode } = useCamMode();
return (
<RenderOverlay>
{selectedUser && (
// eslint-disable-next-line
<div
className="follow-person-container"
onPointerDown={() => {
clearSelectedUser();
setCamMode("FirstPerson");
}}
style={{ "--user-color": selectedUser.color } as React.CSSProperties}
>
<div className="follower-name">Viewing through {selectedUser.name}s eyes</div>
</div>
)}
</RenderOverlay>
);
};
export default FollowPerson;

View File

@@ -0,0 +1,77 @@
import React, { useEffect } from "react";
import RenderOverlay from "./Overlay";
import { LogoIconLarge } from "../icons/Logo";
import { useParams } from "react-router-dom";
import { useProjectName } from "../../store/builder/store";
import { getAllProjects } from "../../services/dashboard/getAllProjects";
import { useComparisonProduct } from "../../store/simulation/useSimulationStore";
interface LoadingPageProps {
progress: number; // Expect progress as a percentage (0-100)
}
const LoadingPage: React.FC<LoadingPageProps> = ({ progress }) => {
const { projectName, setProjectName } = useProjectName();
const { projectId } = useParams();
const { comparisonProduct } = useComparisonProduct();
const validatedProgress = Math.min(100, Math.max(0, progress));
const generateThumbnail = async () => {
const email = localStorage.getItem("email");
const userId = localStorage.getItem("userId");
try {
if (!email || !userId) {
console.error("User data not found in localStorage");
return;
}
const emailParts = email.split("@");
if (emailParts.length < 2) {
console.error("Invalid email format");
return;
}
const domainParts = emailParts[1].split(".");
const Organization = domainParts[0];
const projects = await getAllProjects(userId, Organization);
const filterProject = projects?.Projects.find(
(val: any) => val.projectUuid === projectId || val._id === projectId
);
setProjectName(filterProject.projectName);
} catch {}
};
useEffect(() => {
generateThumbnail();
}, []);
return (
<RenderOverlay>
<div
className={`loading-wrapper ${
comparisonProduct != null ? "comparisionLoading" : ""
}`}
>
<div className="loading-container">
<div className="project-name">{projectName}</div>
<div className="loading-hero-container">
<div className="logo">
<LogoIconLarge />
</div>
<div className="content">Entering A New World of Dwinzo</div>
</div>
<div className="progress-container">
<div className="progress-value">{validatedProgress}%</div>
<div className="progress-indicator-container">
<div
className="progress-bar"
style={{ width: `${validatedProgress}%` }} // Dynamic width
></div>
</div>
</div>
</div>
</div>
</RenderOverlay>
);
};
export default LoadingPage;

View File

@@ -0,0 +1,21 @@
import { ReactNode } from "react";
import ReactDOM from "react-dom";
// Define the props interface for the component, which requires `children` as a ReactNode type
interface PortalProps {
children: ReactNode;
}
// The `RenderOverlay` component allows rendering of its children into a specific DOM node (`root-over`) outside of the regular React component tree
const RenderOverlay = ({ children }: PortalProps) => {
// Retrieve the DOM element with the id `root-over` which serves as the mounting point for the overlay content.
const rootOver = document.getElementById("root-over");
// If the `rootOver` element exists in the DOM, use `ReactDOM.createPortal` to render the `children` inside this element
// `ReactDOM.createPortal` allows rendering outside of the usual React component tree, useful for overlays, modals, and tooltips
// If `rootOver` is null (i.e., does not exist), return null, rendering nothing
return rootOver ? ReactDOM.createPortal(children, rootOver) : null;
};
// Export the `RenderOverlay` component as the default export for use in other parts of the application
export default RenderOverlay;

View File

@@ -0,0 +1,79 @@
import React from "react";
// Define the prop types
interface SkeletonUIProps {
type: "asset" | "assetLibrary" | "assetWidget" | "default"; // You can expand this with other types as needed
}
// Define the SkeletonUI component
const SkeletonUI: React.FC<SkeletonUIProps> = ({ type }) => {
// Function to render skeleton content based on 'type'
const renderSkeleton = () => {
switch (type) {
case "assetLibrary":
return (
<>
{Array(5)
.fill(null) // Create an array of 5 empty items
.map((_, index) => (
<div key={index} className="skeleton-content">
<div className="skeleton asset-image"></div>
<div className="skeleton asset-details"></div>
<div className="skeleton organization"></div>
<div className="skeleton asset-review"></div>
<div className="skeleton button"></div>
</div>
))}
</>
);
case "assetWidget":
return (
<div className="skeleton-content">
<div className="skeleton skeleton-widget"></div>
<div className="skeleton skeleton-widget"></div>
</div>
);
case "asset":
return (
<>
<div className="skeleton asset-category-title"></div>
<div className="skeleton-content-asset">
<div className="skeleton-content">
<div className="skeleton asset-name"></div>
<div className="skeleton asset"></div>
</div>
<div className="skeleton-content">
<div className="skeleton asset-name"></div>
<div className="skeleton asset"></div>
</div>
<div className="skeleton-content">
<div className="skeleton asset-name"></div>
<div className="skeleton asset"></div>
</div>
<div className="skeleton-content">
<div className="skeleton asset-name"></div>
<div className="skeleton asset"></div>
</div>
</div>
</>
);
default:
return (
<div className="skeleton-content">
<div className="skeleton-header">
<div className="skeleton skeleton-title"></div>
<div className="skeleton skeleton-subtitle"></div>
</div>
<div className="skeleton skeleton-card"></div>
<div className="skeleton skeleton-card"></div>
</div>
);
}
};
return <div className="skeleton-wrapper">{renderSkeleton()}</div>;
};
export default SkeletonUI;

View File

@@ -0,0 +1,74 @@
import React, { createContext, useContext, useState, ReactNode } from "react";
type Toast = {
id: string;
message: string;
type: "success" | "error" | "info" | "warning";
position?: ToastPosition; // Optional position for each toast
};
type ToastPosition =
| "top-left"
| "top-center"
| "top-right"
| "bottom-left"
| "bottom-center"
| "bottom-right";
type ToastContextType = {
addToast: (
message: string,
type: Toast["type"],
position?: ToastPosition
) => void;
removeToast: (id: string) => void;
};
const ToastContext = createContext<ToastContextType | undefined>(undefined);
const ToastProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [toasts, setToasts] = useState<Toast[]>([]);
const addToast = (
message: string,
type: Toast["type"],
position: ToastPosition = "bottom-center" // Default position
) => {
const id = Math.random().toString(36).substr(2, 9); // Generate a unique ID
setToasts((prev) => [...prev, { id, message, type, position }]);
// Auto-remove toast after 3 seconds
setTimeout(() => removeToast(id), 3000);
};
const removeToast = (id: string) => {
setToasts((prev) => prev.filter((toast) => toast.id !== id));
};
return (
<ToastContext.Provider value={{ addToast, removeToast }}>
{children}
<div className={`toast-container ${"bottom-center"}`}>
{toasts.map((toast) => (
<div
key={toast.id}
className={`toast ${toast.type}`}
onClick={() => removeToast(toast.id)} // Allow manual dismissal
>
{toast.message}
</div>
))}
</div>
</ToastContext.Provider>
);
};
export const useToast = (): ToastContextType => {
const context = useContext(ToastContext);
if (!context) {
throw new Error("useToast must be used within a ToastProvider");
}
return context;
};
export default ToastProvider;