Merge remote-tracking branch 'origin/dev' into dropdownButton_component
This commit is contained in:
commit
d2eb87d44b
11 changed files with 174 additions and 65 deletions
|
|
@ -7,8 +7,17 @@ import { alertContext } from "../../contexts/alertContext";
|
||||||
import { AuthContext } from "../../contexts/authContext";
|
import { AuthContext } from "../../contexts/authContext";
|
||||||
import { darkContext } from "../../contexts/darkContext";
|
import { darkContext } from "../../contexts/darkContext";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
|
import { gradients } from "../../utils/styleUtils";
|
||||||
import IconComponent from "../genericIconComponent";
|
import IconComponent from "../genericIconComponent";
|
||||||
import { Button } from "../ui/button";
|
import { Button } from "../ui/button";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "../ui/dropdown-menu";
|
||||||
import { Separator } from "../ui/separator";
|
import { Separator } from "../ui/separator";
|
||||||
import MenuBar from "./components/menuBar";
|
import MenuBar from "./components/menuBar";
|
||||||
|
|
||||||
|
|
@ -17,7 +26,7 @@ export default function Header(): JSX.Element {
|
||||||
const { dark, setDark } = useContext(darkContext);
|
const { dark, setDark } = useContext(darkContext);
|
||||||
const { notificationCenter } = useContext(alertContext);
|
const { notificationCenter } = useContext(alertContext);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { logout, autoLogin, isAdmin } = useContext(AuthContext);
|
const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
|
||||||
const { stars } = useContext(darkContext);
|
const { stars } = useContext(darkContext);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
@ -31,40 +40,6 @@ export default function Header(): JSX.Element {
|
||||||
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
|
{flows.findIndex((f) => tabId === f.id) !== -1 && tabId !== "" && (
|
||||||
<MenuBar flows={flows} tabId={tabId} />
|
<MenuBar flows={flows} tabId={tabId} />
|
||||||
)}
|
)}
|
||||||
{!autoLogin && location.pathname !== `/flow/${tabId}` && (
|
|
||||||
<a
|
|
||||||
onClick={() => {
|
|
||||||
logout();
|
|
||||||
navigate("/login");
|
|
||||||
}}
|
|
||||||
className="mx-5 cursor-pointer text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
|
|
||||||
>
|
|
||||||
Sign out
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{location.pathname === "/admin" && (
|
|
||||||
<a
|
|
||||||
onClick={() => {
|
|
||||||
navigate("/");
|
|
||||||
}}
|
|
||||||
className="cursor-pointer text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
|
|
||||||
>
|
|
||||||
Home
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{isAdmin &&
|
|
||||||
!autoLogin &&
|
|
||||||
location.pathname !== "/admin" &&
|
|
||||||
location.pathname !== `/flow/${tabId}` && (
|
|
||||||
<a
|
|
||||||
className="cursor-pointer text-sm font-medium text-muted-foreground transition-colors hover:text-primary"
|
|
||||||
onClick={() => navigate("/admin")}
|
|
||||||
>
|
|
||||||
Admin page
|
|
||||||
</a>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="round-button-div">
|
<div className="round-button-div">
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
|
|
@ -156,6 +131,40 @@ export default function Header(): JSX.Element {
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{!autoLogin && (
|
||||||
|
<>
|
||||||
|
<Separator orientation="vertical" />
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<button
|
||||||
|
className={
|
||||||
|
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
||||||
|
gradients[
|
||||||
|
parseInt(userData?.id ?? "", 10) % gradients.length
|
||||||
|
]
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent>
|
||||||
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
{isAdmin && (
|
||||||
|
<DropdownMenuItem onClick={() => navigate("/admin")}>
|
||||||
|
Admin Page
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={() => {
|
||||||
|
logout();
|
||||||
|
navigate("/login");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Sign Out
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ export default function InputComponent({
|
||||||
{isForm ? (
|
{isForm ? (
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
<Input
|
<Input
|
||||||
|
type={password && !pwdVisible ? "password" : "text"}
|
||||||
value={value}
|
value={value}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
required={required}
|
required={required}
|
||||||
|
|
@ -53,6 +54,7 @@ export default function InputComponent({
|
||||||
</Form.Control>
|
</Form.Control>
|
||||||
) : (
|
) : (
|
||||||
<Input
|
<Input
|
||||||
|
type={password && !pwdVisible ? "password" : "text"}
|
||||||
value={value}
|
value={value}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
required={required}
|
required={required}
|
||||||
|
|
@ -76,6 +78,8 @@ export default function InputComponent({
|
||||||
)}
|
)}
|
||||||
{password && (
|
{password && (
|
||||||
<button
|
<button
|
||||||
|
type="button"
|
||||||
|
tabIndex={-1}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
editNode
|
editNode
|
||||||
? "input-component-true-button"
|
? "input-component-true-button"
|
||||||
|
|
|
||||||
16
src/frontend/src/components/skeletonCardComponent/index.tsx
Normal file
16
src/frontend/src/components/skeletonCardComponent/index.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Skeleton } from "../ui/skeleton";
|
||||||
|
|
||||||
|
export const SkeletonCardComponent = (): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div className="skeleton-card">
|
||||||
|
<div className="skeleton-card-wrapper">
|
||||||
|
<Skeleton className="h-8 w-8 rounded-full" />
|
||||||
|
<Skeleton className="h-4 w-[120px]" />
|
||||||
|
</div>
|
||||||
|
<div className="skeleton-card-text">
|
||||||
|
<Skeleton className="h-3 w-[250px]" />
|
||||||
|
<Skeleton className="h-3 w-[200px]" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
15
src/frontend/src/components/ui/skeleton.tsx
Normal file
15
src/frontend/src/components/ui/skeleton.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { cn } from "../../utils/utils"
|
||||||
|
|
||||||
|
function Skeleton({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.HTMLAttributes<HTMLDivElement>) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn("animate-pulse rounded-md bg-border", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Skeleton }
|
||||||
|
|
@ -39,6 +39,7 @@ const TabsContextInitialValue: TabsContextType = {
|
||||||
save: () => {},
|
save: () => {},
|
||||||
tabId: "",
|
tabId: "",
|
||||||
setTabId: (index: string) => {},
|
setTabId: (index: string) => {},
|
||||||
|
isLoading: true,
|
||||||
flows: [],
|
flows: [],
|
||||||
removeFlow: (id: string) => {},
|
removeFlow: (id: string) => {},
|
||||||
addFlow: async (flowData?: any) => "",
|
addFlow: async (flowData?: any) => "",
|
||||||
|
|
@ -72,10 +73,12 @@ export const TabsContext = createContext<TabsContextType>(
|
||||||
export function TabsProvider({ children }: { children: ReactNode }) {
|
export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
const { setErrorData, setNoticeData, setSuccessData } =
|
const { setErrorData, setNoticeData, setSuccessData } =
|
||||||
useContext(alertContext);
|
useContext(alertContext);
|
||||||
const { getAuthentication } = useContext(AuthContext);
|
const { getAuthentication, isAuthenticated } = useContext(AuthContext);
|
||||||
|
|
||||||
const [tabId, setTabId] = useState("");
|
const [tabId, setTabId] = useState("");
|
||||||
|
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
|
||||||
const [flows, setFlows] = useState<Array<FlowType>>([]);
|
const [flows, setFlows] = useState<Array<FlowType>>([]);
|
||||||
const [id, setId] = useState(uid());
|
const [id, setId] = useState(uid());
|
||||||
const { templates, reactFlowInstance } = useContext(typesContext);
|
const { templates, reactFlowInstance } = useContext(typesContext);
|
||||||
|
|
@ -86,6 +89,12 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
const [tabsState, setTabsState] = useState<TabsState>({});
|
const [tabsState, setTabsState] = useState<TabsState>({});
|
||||||
const [getTweak, setTweak] = useState<tweakType>([]);
|
const [getTweak, setTweak] = useState<tweakType>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isAuthenticated) {
|
||||||
|
hardReset();
|
||||||
|
}
|
||||||
|
}, [isAuthenticated]);
|
||||||
|
|
||||||
const newNodeId = useRef(uid());
|
const newNodeId = useRef(uid());
|
||||||
function incrementNodeId() {
|
function incrementNodeId() {
|
||||||
newNodeId.current = uid();
|
newNodeId.current = uid();
|
||||||
|
|
@ -116,11 +125,13 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshFlows() {
|
function refreshFlows() {
|
||||||
|
setIsLoading(true);
|
||||||
getTabsDataFromDB().then((DbData) => {
|
getTabsDataFromDB().then((DbData) => {
|
||||||
if (DbData && Object.keys(templates).length > 0) {
|
if (DbData && Object.keys(templates).length > 0) {
|
||||||
try {
|
try {
|
||||||
processDBData(DbData);
|
processDBData(DbData);
|
||||||
updateStateWithDbData(DbData);
|
updateStateWithDbData(DbData);
|
||||||
|
setIsLoading(false);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -229,6 +240,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
setTabId("");
|
setTabId("");
|
||||||
|
|
||||||
setFlows([]);
|
setFlows([]);
|
||||||
|
setIsLoading(true);
|
||||||
setId(uid());
|
setId(uid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -641,6 +653,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
||||||
paste,
|
paste,
|
||||||
getTweak,
|
getTweak,
|
||||||
setTweak,
|
setTweak,
|
||||||
|
isLoading,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,21 @@ import { Link, useNavigate } from "react-router-dom";
|
||||||
import { CardComponent } from "../../components/cardComponent";
|
import { CardComponent } from "../../components/cardComponent";
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
import IconComponent from "../../components/genericIconComponent";
|
||||||
import Header from "../../components/headerComponent";
|
import Header from "../../components/headerComponent";
|
||||||
|
import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
||||||
import { Button } from "../../components/ui/button";
|
import { Button } from "../../components/ui/button";
|
||||||
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
||||||
import { TabsContext } from "../../contexts/tabsContext";
|
import { TabsContext } from "../../contexts/tabsContext";
|
||||||
import DropdownButton from "../../components/DropdownButtonComponent";
|
import DropdownButton from "../../components/DropdownButtonComponent";
|
||||||
export default function HomePage(): JSX.Element {
|
export default function HomePage(): JSX.Element {
|
||||||
const { flows, setTabId, downloadFlows, uploadFlows, addFlow, removeFlow, uploadFlow } =
|
const {
|
||||||
useContext(TabsContext);
|
flows,
|
||||||
|
setTabId,
|
||||||
|
downloadFlows,
|
||||||
|
uploadFlows,
|
||||||
|
addFlow,
|
||||||
|
removeFlow, uploadFlow,
|
||||||
|
isLoading,
|
||||||
|
} = useContext(TabsContext);
|
||||||
const dropdownOptions = [{name: "Import from JSON", onBtnClick: () => uploadFlow(true)}]
|
const dropdownOptions = [{name: "Import from JSON", onBtnClick: () => uploadFlow(true)}]
|
||||||
|
|
||||||
// Set a null id
|
// Set a null id
|
||||||
|
|
@ -18,6 +26,10 @@ export default function HomePage(): JSX.Element {
|
||||||
}, []);
|
}, []);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log(isLoading);
|
||||||
|
}, [isLoading]);
|
||||||
|
|
||||||
// Personal flows display
|
// Personal flows display
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -62,7 +74,15 @@ export default function HomePage(): JSX.Element {
|
||||||
Manage your personal projects. Download or upload your collection.
|
Manage your personal projects. Download or upload your collection.
|
||||||
</span>
|
</span>
|
||||||
<div className="main-page-flows-display">
|
<div className="main-page-flows-display">
|
||||||
{flows.map((flow, idx) => (
|
{isLoading && flows.length == 0 ? (
|
||||||
|
<>
|
||||||
|
<SkeletonCardComponent />
|
||||||
|
<SkeletonCardComponent />
|
||||||
|
<SkeletonCardComponent />
|
||||||
|
<SkeletonCardComponent />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
flows.map((flow, idx) => (
|
||||||
<CardComponent
|
<CardComponent
|
||||||
key={idx}
|
key={idx}
|
||||||
flow={flow}
|
flow={flow}
|
||||||
|
|
@ -86,7 +106,8 @@ export default function HomePage(): JSX.Element {
|
||||||
removeFlow(flow.id);
|
removeFlow(flow.id);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ export default function LoginPage(): JSX.Element {
|
||||||
|
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
<Input
|
<Input
|
||||||
|
type="username"
|
||||||
onChange={({ target: { value } }) => {
|
onChange={({ target: { value } }) => {
|
||||||
handleInput({ target: { name: "username", value } });
|
handleInput({ target: { name: "username", value } });
|
||||||
}}
|
}}
|
||||||
|
|
@ -130,12 +131,12 @@ export default function LoginPage(): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Form.Submit asChild>
|
<Form.Submit asChild>
|
||||||
<Button className="mr-3 mt-6 w-full">Sign in</Button>
|
<Button className="mr-3 mt-6 w-full" type="submit">Sign in</Button>
|
||||||
</Form.Submit>
|
</Form.Submit>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Link to="/signup">
|
<Link to="/signup">
|
||||||
<Button className="w-full" variant="outline">
|
<Button className="w-full" variant="outline" type="button">
|
||||||
Don't have an account? <b>Sign Up</b>
|
Don't have an account? <b>Sign Up</b>
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ export default function SignUp(): JSX.Element {
|
||||||
|
|
||||||
<Form.Control asChild>
|
<Form.Control asChild>
|
||||||
<Input
|
<Input
|
||||||
|
type="username"
|
||||||
onChange={({ target: { value } }) => {
|
onChange={({ target: { value } }) => {
|
||||||
handleInput({ target: { name: "username", value } });
|
handleInput({ target: { name: "username", value } });
|
||||||
}}
|
}}
|
||||||
|
|
@ -157,6 +158,7 @@ export default function SignUp(): JSX.Element {
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<Form.Submit asChild>
|
<Form.Submit asChild>
|
||||||
<Button
|
<Button
|
||||||
|
type="submit"
|
||||||
className="mr-3 mt-6 w-full"
|
className="mr-3 mt-6 w-full"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleSignup();
|
handleSignup();
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,18 @@
|
||||||
@apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
|
@apply form-input block w-full truncate rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skeleton-card {
|
||||||
|
@apply bg-background h-48 p-4 border rounded-lg flex flex-col gap-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-card-wrapper {
|
||||||
|
@apply flex items-center space-x-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skeleton-card-text {
|
||||||
|
@apply flex flex-col gap-3;
|
||||||
|
}
|
||||||
|
|
||||||
/* The same as primary-input but no-truncate */
|
/* The same as primary-input but no-truncate */
|
||||||
.textarea-primary {
|
.textarea-primary {
|
||||||
@apply form-input block w-full rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
|
@apply form-input block w-full rounded-md border-border bg-background px-3 text-left shadow-sm placeholder:text-muted-foreground focus:border-ring focus:placeholder-transparent focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 sm:text-sm;
|
||||||
|
|
|
||||||
|
|
@ -36,3 +36,18 @@ pre {
|
||||||
.gradient-start {
|
.gradient-start {
|
||||||
animation: gradient-motion-start 4s infinite forwards;
|
animation: gradient-motion-start 4s infinite forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input:-webkit-autofill,
|
||||||
|
input:-webkit-autofill:hover,
|
||||||
|
input:-webkit-autofill:focus,
|
||||||
|
textarea:-webkit-autofill,
|
||||||
|
textarea:-webkit-autofill:hover,
|
||||||
|
textarea:-webkit-autofill:focus,
|
||||||
|
select:-webkit-autofill,
|
||||||
|
select:-webkit-autofill:hover,
|
||||||
|
select:-webkit-autofill:focus {
|
||||||
|
-webkit-text-fill-color: black;
|
||||||
|
-webkit-box-shadow: 0 0 0px 1000px #fff6d0 inset;
|
||||||
|
box-shadow: 0 0 0px 1000px #fff6d0 inset;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ export type TabsContextType = {
|
||||||
saveFlow: (flow: FlowType) => Promise<void>;
|
saveFlow: (flow: FlowType) => Promise<void>;
|
||||||
save: () => void;
|
save: () => void;
|
||||||
tabId: string;
|
tabId: string;
|
||||||
|
isLoading: boolean;
|
||||||
setTabId: (index: string) => void;
|
setTabId: (index: string) => void;
|
||||||
flows: Array<FlowType>;
|
flows: Array<FlowType>;
|
||||||
removeFlow: (id: string) => void;
|
removeFlow: (id: string) => void;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue