Improvements in skill tree

This commit is contained in:
ITQ
2024-04-03 01:15:42 +03:00
parent 2f8fcdcc93
commit 0bbc99c927
13 changed files with 511 additions and 60 deletions
+35
View File
@@ -24,6 +24,7 @@
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.19",
"class-variance-authority": "^0.7.0",
@@ -1926,6 +1927,40 @@
}
}
},
"node_modules/@radix-ui/react-toast": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.1.5.tgz",
"integrity": "sha512-fRLn227WHIBRSzuRzGJ8W+5YALxofH23y0MlPLddaIpLpCDqdE0NZlS2NRQDRiptfxDeeCjgFIpexB1/zkxDlw==",
"dependencies": {
"@babel/runtime": "^7.13.10",
"@radix-ui/primitive": "1.0.1",
"@radix-ui/react-collection": "1.0.3",
"@radix-ui/react-compose-refs": "1.0.1",
"@radix-ui/react-context": "1.0.1",
"@radix-ui/react-dismissable-layer": "1.0.5",
"@radix-ui/react-portal": "1.0.4",
"@radix-ui/react-presence": "1.0.1",
"@radix-ui/react-primitive": "1.0.3",
"@radix-ui/react-use-callback-ref": "1.0.1",
"@radix-ui/react-use-controllable-state": "1.0.1",
"@radix-ui/react-use-layout-effect": "1.0.1",
"@radix-ui/react-visually-hidden": "1.0.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0",
"react-dom": "^16.8 || ^17.0 || ^18.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
+1
View File
@@ -26,6 +26,7 @@
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.19",
"class-variance-authority": "^0.7.0",
+1 -1
View File
@@ -119,7 +119,7 @@ export const Navbar = () => {
to={"login"}
className={`border ${buttonVariants({ variant: "default" })}`}
>
Login
Signup
</Link>
</div>
</NavigationMenuList>
+1 -1
View File
@@ -25,7 +25,7 @@ const teamList: TeamProps[] = [
{
imageUrl: "https://raw.githubusercontent.com/devitq/devitq/main/logo.png",
name: "ITQ",
position: "Backend & Frontend Developer & Dev OPS",
position: "Backend & Frontend Developer",
socialNetworks: [
{ name: "GitHub", url: "https://github.com/devitq" },
],
+40
View File
@@ -0,0 +1,40 @@
import React, { Component, RefObject } from "react";
import OrgChart from "@balkangraph/orgchart.js";
interface Node {
name: string;
img: string;
// Add more fields if needed
}
interface ChartProps {
nodes: Node[];
}
export default class Chart extends Component<ChartProps> {
private divRef: RefObject<HTMLDivElement>;
private chart: any;
constructor(props: ChartProps) {
super(props);
this.divRef = React.createRef();
}
shouldComponentUpdate() {
return false;
}
componentDidMount() {
this.chart = new OrgChart(this.divRef.current!, {
nodes: this.props.nodes,
nodeBinding: {
field_0: "name",
img_0: "img",
},
});
}
render() {
return <div id="tree" ref={this.divRef}></div>;
}
}
@@ -1,12 +1,10 @@
import { About } from "../../About";
import { FAQ } from "../../FAQ";
import { Features } from "../../Features";
import { Footer } from "../../Footer";
import { Hero } from "../../Hero";
import { HowItWorks } from "../../HowItWorks";
import { Navbar } from "../../Navbar";
import { ScrollToTop } from "../../ScrollToTop";
import { Services } from "../../Services";
import { Team } from "../../Team";
import "../../../App.css";
import { ThemeProvider } from "../../theme-provider.tsx";
@@ -18,8 +16,6 @@ function Landing() {
<Hero />
<About />
<HowItWorks />
{/* <Features />
<Services /> */}
<Team />
<FAQ />
<Footer />
@@ -1,28 +1,77 @@
import { Input } from "../../ui/input";
import VacancyCard from "../../entities/VacancyCard/VacancyCard";
import { Textarea } from "../../shared/ui/textarea";
import { Label } from "../../shared/ui/label";
import { Button } from "../../shared/ui/button";
import less from "./SkillTree.module.less"
import { t } from "i18next";
import less from "./SkillTree.module.less";
import { Switch } from "../../shared/ui/switch";
import { addEvent } from "../../widgets/Header/AuthAPI";
import { Link } from "react-router-dom";
import { ToastAction } from "../../shared/ui/toast"
import { useToast } from "../../shared/ui/use-toast"
import { buttonVariants } from "../../ui/button";
const SkillTree = () => {
const { toast } = useToast()
return (
<div className={less["general"]}>
<div className={`${less["general"]} container`}>
<div className={less["left"]}>
<form className={less["input-form"]} onSubmit={(event) => addEvent(event)}>
<Input type="text" name="title" placeholder="Event name" />
<Input type="text" name="description" placeholder="Last name" />
<Input type="date" name="start_date" placeholder="Start date" />
<Input type="date" name="end_date" placeholder="End date" />
<Textarea name="description" placeholder="Description" />
<Switch/>
<Button>{t("buttonLoginInSystem")}</Button>
<Link
to={"/dash/admin"}
className={`border ${buttonVariants({ variant: "outline" })} mb-4`}
>
Back
</Link>
<h3
className={"text-2xl font-semibold leading-none tracking-tight mb-5"}
>
Create event
</h3>
<form
className={less["input-form"]}
onSubmit={(event) => {
addEvent(event);
toast({
title: "Scheduled: Catch up ",
description: "Friday, February 10, 2023 at 5:57 PM",
action: (
<ToastAction altText="Goto schedule to undo">Undo</ToastAction>
),
});
}}
>
<Input
type="text"
name="title"
placeholder="Event name"
className="mb-3"
/>
<Input
type="date"
name="start_date"
placeholder="Start date"
className="mb-3"
/>
<Input
type="date"
name="end_date"
placeholder="End date"
className="mb-3"
/>
<Textarea
name="description"
placeholder="Description"
className="mb-3"
/>
<div className="flex items-center space-x-2">
<Switch className="mb-3" id="is_online" />
<Label htmlFor="is_online">Is online</Label>
</div>
<br />
<Button>Create</Button>
</form>
</div>
<div className={less["right"]}>
@@ -31,5 +80,5 @@ const SkillTree = () => {
</div>
</div>
);
}
};
export default SkillTree;
+1 -1
View File
@@ -4,7 +4,7 @@ import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "../../../../lib/utils"
import { cn } from "/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+1 -1
View File
@@ -3,7 +3,7 @@
import * as React from "react"
import * as SwitchPrimitives from "@radix-ui/react-switch"
import { cn } from "../../../../lib/utils"
import { cn } from "../../../lib/utils"
const Switch = React.forwardRef<
React.ElementRef<typeof SwitchPrimitives.Root>,
+129
View File
@@ -0,0 +1,129 @@
"use client"
import * as React from "react"
import { Cross2Icon } from "@radix-ui/react-icons"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "/lib/utils"
const ToastProvider = ToastPrimitives.Provider
const ToastViewport = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Viewport>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Viewport
ref={ref}
className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className
)}
{...props}
/>
))
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
{
variants: {
variant: {
default: "border bg-background text-foreground",
destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
return (
<ToastPrimitives.Root
ref={ref}
className={cn(toastVariants({ variant }), className)}
{...props}
/>
)
})
Toast.displayName = ToastPrimitives.Root.displayName
const ToastAction = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Action>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Action
ref={ref}
className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className
)}
{...props}
/>
))
ToastAction.displayName = ToastPrimitives.Action.displayName
const ToastClose = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Close>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Close
ref={ref}
className={cn(
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
className
)}
toast-close=""
{...props}
>
<Cross2Icon className="h-4 w-4" />
</ToastPrimitives.Close>
))
ToastClose.displayName = ToastPrimitives.Close.displayName
const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title
ref={ref}
className={cn("text-sm font-semibold [&+div]:text-xs", className)}
{...props}
/>
))
ToastTitle.displayName = ToastPrimitives.Title.displayName
const ToastDescription = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Description>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Description
ref={ref}
className={cn("text-sm opacity-90", className)}
{...props}
/>
))
ToastDescription.displayName = ToastPrimitives.Description.displayName
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
type ToastActionElement = React.ReactElement<typeof ToastAction>
export {
type ToastProps,
type ToastActionElement,
ToastProvider,
ToastViewport,
Toast,
ToastTitle,
ToastDescription,
ToastClose,
ToastAction,
}
@@ -0,0 +1,35 @@
"use client"
import {
Toast,
ToastClose,
ToastDescription,
ToastProvider,
ToastTitle,
ToastViewport,
} from "src/components/shared/ui/toast"
import { useToast } from "src/components/shared/ui/use-toast"
export function Toaster() {
const { toasts } = useToast()
return (
<ToastProvider>
{toasts.map(function ({ id, title, description, action, ...props }) {
return (
<Toast key={id} {...props}>
<div className="grid gap-1">
{title && <ToastTitle>{title}</ToastTitle>}
{description && (
<ToastDescription>{description}</ToastDescription>
)}
</div>
{action}
<ToastClose />
</Toast>
)
})}
<ToastViewport />
</ToastProvider>
)
}
@@ -0,0 +1,194 @@
"use client"
// Inspired by react-hot-toast library
import * as React from "react"
import type {
ToastActionElement,
ToastProps,
} from "src/components/shared/ui/toast"
const TOAST_LIMIT = 1
const TOAST_REMOVE_DELAY = 1000000
type ToasterToast = ToastProps & {
id: string
title?: React.ReactNode
description?: React.ReactNode
action?: ToastActionElement
}
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const
let count = 0
function genId() {
count = (count + 1) % Number.MAX_SAFE_INTEGER
return count.toString()
}
type ActionType = typeof actionTypes
type Action =
| {
type: ActionType["ADD_TOAST"]
toast: ToasterToast
}
| {
type: ActionType["UPDATE_TOAST"]
toast: Partial<ToasterToast>
}
| {
type: ActionType["DISMISS_TOAST"]
toastId?: ToasterToast["id"]
}
| {
type: ActionType["REMOVE_TOAST"]
toastId?: ToasterToast["id"]
}
interface State {
toasts: ToasterToast[]
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
const addToRemoveQueue = (toastId: string) => {
if (toastTimeouts.has(toastId)) {
return
}
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId)
dispatch({
type: "REMOVE_TOAST",
toastId: toastId,
})
}, TOAST_REMOVE_DELAY)
toastTimeouts.set(toastId, timeout)
}
export const reducer = (state: State, action: Action): State => {
switch (action.type) {
case "ADD_TOAST":
return {
...state,
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
}
case "UPDATE_TOAST":
return {
...state,
toasts: state.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t
),
}
case "DISMISS_TOAST": {
const { toastId } = action
// ! Side effects ! - This could be extracted into a dismissToast() action,
// but I'll keep it here for simplicity
if (toastId) {
addToRemoveQueue(toastId)
} else {
state.toasts.forEach((toast) => {
addToRemoveQueue(toast.id)
})
}
return {
...state,
toasts: state.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t
),
}
}
case "REMOVE_TOAST":
if (action.toastId === undefined) {
return {
...state,
toasts: [],
}
}
return {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
}
}
}
const listeners: Array<(state: State) => void> = []
let memoryState: State = { toasts: [] }
function dispatch(action: Action) {
memoryState = reducer(memoryState, action)
listeners.forEach((listener) => {
listener(memoryState)
})
}
type Toast = Omit<ToasterToast, "id">
function toast({ ...props }: Toast) {
const id = genId()
const update = (props: ToasterToast) =>
dispatch({
type: "UPDATE_TOAST",
toast: { ...props, id },
})
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
dispatch({
type: "ADD_TOAST",
toast: {
...props,
id,
open: true,
onOpenChange: (open) => {
if (!open) dismiss()
},
},
})
return {
id: id,
dismiss,
update,
}
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState)
React.useEffect(() => {
listeners.push(setState)
return () => {
const index = listeners.indexOf(setState)
if (index > -1) {
listeners.splice(index, 1)
}
}
}, [state])
return {
...state,
toast,
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
}
}
export { useToast, toast }
-28
View File
@@ -1,28 +0,0 @@
import React, { Component } from "react";
import OrgChart from "@balkangraph/orgchart.js";
export default class Chart extends Component {
constructor(props) {
super(props);
this.divRef = React.createRef();
}
shouldComponentUpdate() {
return false;
}
componentDidMount() {
this.chart = new OrgChart(this.divRef.current, {
nodes: this.props.nodes,
nodeBinding: {
field_0: "name",
img_0: "img",
},
});
}
render() {
return <div id="tree" ref={this.divRef}></div>;
}
}