Skip to content

Commit

Permalink
Feature/projects (#15)
Browse files Browse the repository at this point in the history
* add project-page UI

* update icons source to react-icon

* fix(mock): updated mock getMock

* delete project-page

* update project page

* update (proejcts): changed some styling

* fix(projects): added tags

* wip(filterstore): projects filterstore wip

* feat(projects): projects overview and global filtering

* fix(filterstore): updated filterstore with filterobject

* wip(filterscreen): added filterscreen in the interactive projects overview

* fix(filters): updated filters through ui

* wip(filtersidebar): changed sidebar filter to use react-hook-form and no more localstate

* fix(filters): updated filters in projects with new filterscreen

* fix(filtering): adjusted reset filter states

* fix(scroll): added scrolling bar in projects overview and tags in legend based on maintag

---------

Co-authored-by: Haxfx <[email protected]>
  • Loading branch information
karim-mev and Haxfx authored Mar 30, 2024
1 parent e4fed7f commit 44b2298
Show file tree
Hide file tree
Showing 37 changed files with 758 additions and 414 deletions.
4 changes: 1 addition & 3 deletions app/(app)/ecosystem/ChartSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
export const ChartSkeleton = () => {
return (
<div className="skeleton-container flex h-full w-full flex-col items-center justify-center gap-4 overflow-hidden bg-primary text-center text-gray-800">
<p>
With a large amount of nodes it might take time for the graph to load.
</p>
<p>Loading the visualization of projects in the SEI Ecosystem</p>
</div>
)
}
8 changes: 6 additions & 2 deletions app/(app)/ecosystem/RelationsChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { DetailScreen, useRelationChartState } from "@/components/d3"
import { Project } from "@/database/schemas/projects.schema"

import { FilterScreen } from "@/components/filter"
import ChartWrapper from "./ChartWrapper"

export type TProps = {
Expand All @@ -15,12 +16,15 @@ const RelationsChart = ({ nodes, loading }: TProps) => {

return (
<div className='left-0" absolute top-0 flex h-full w-full overflow-hidden bg-[#f3f4f6]'>
{chart! && (
<>
<FilterScreen close={true} />
</>
)}
<ChartWrapper nodes={nodes} loading={loading} />
{chart! && (
<>
<DetailScreen nodes={nodes} />
{/* <ContextMenu /> */}
{/* <HoverToolTip /> */}
</>
)}
</div>
Expand Down
12 changes: 9 additions & 3 deletions app/(app)/ecosystem/RelationsContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
"use client"

import React, { Suspense } from "react"

import { mockProjects } from "@/mocks/projects.mocks"
import { useFilterStore } from "@/stores/project-filter-store"
import { ChartSkeleton } from "./ChartSkeleton"
import RelationsChart from "./RelationsChart"

export const RelationsContainer = () => {
const generateMockData2 = mockProjects(100)
const { projects, filteredProjects, setProjects } = useFilterStore()
if (projects.length === 0) {
setProjects(mockProjects(100))
}

const [nodes] = React.useMemo(() => {
const nodes = generateMockData2
const nodes = filteredProjects

return [nodes] as const
}, [generateMockData2])
}, [filteredProjects])

return (
<Suspense fallback={<ChartSkeleton />}>
Expand Down
89 changes: 89 additions & 0 deletions app/(app)/projects/ProjectsFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"use client"

import { FilterScreen } from "@/components/filter"
import Spinner from "@/components/spinner"
import { AspectRatio } from "@/components/ui/aspect-ratio"
import { Project } from "@/database/schemas/projects.schema"
import { useFilterStore } from "@/stores/project-filter-store"
import Image from "next/image"
import Link from "next/link"
import React, { useEffect } from "react"

export default function FilterProjects({
initialProjects,
}: {
initialProjects: Project[]
}) {
const {
projects,
filter,
resetFilters,
setProjects,
updateFilter,
filteredProjects,
isLoading,
} = useFilterStore()

useEffect(() => {
// Fetch and set projects from db here
if (projects.length === 0) {
setProjects(initialProjects)
}
}, [setProjects])

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
updateFilter({ searchTerm: event.target.value })
}

if (isLoading) {
return <Spinner />
}

return (
<div className="flex h-[85vh] gap-4 overflow-hidden">
<div className="flex w-[23.2rem] items-center gap-4">
<FilterScreen close={false} />
</div>

{filteredProjects.length === 0 ? (
<div className="mt-10 flex w-full justify-center">
No Projects found
</div>
) : (
<div className="projects-grid overflow-y-scroll">
<div className="grid grid-cols-2 gap-4 overflow-hidden md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
{filteredProjects.map((project) => (
<Link
key={project.id}
href={`/projects/${project.slug}`}
className="flex flex-col rounded-md shadow-xl"
>
<AspectRatio ratio={1} className="relative">
<Image
src={"/images/image1.png"}
alt={project.name}
fill
className="rounded-t-md"
/>
<div className="absolute right-4 top-4 bg-white px-2 py-1 text-xs text-neutral-950 opacity-90">
{project.mainTag}
</div>
</AspectRatio>
<div className="flex items-center gap-4 bg-neutral-800 px-4 py-2 text-center">
<Image
src={"/images/sei.jpg"}
alt={project.name}
width={30}
height={30}
className="rounded-full"
/>
<p className="text-sm font-medium">{project.name}</p>
</div>
</Link>
))}
</div>
</div>
)}
</div>
)
}
26 changes: 26 additions & 0 deletions app/(app)/projects/TagFilter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Button } from "@/components/ui/button"
import { projectTagSchema } from "@/database/schemas/projects.schema"
import { useFilterStore } from "@/stores/project-filter-store"

const TagFilter = () => {
const { filter, toggleMainTagFilter } = useFilterStore()

console.log("filter", filter)

return (
<div className="flex h-44 w-full flex-wrap gap-2 leading-none">
{projectTagSchema.options.map((tag) => (
<Button
key={tag}
variant="outline"
onClick={() => toggleMainTagFilter(tag)}
className={`my-1 w-auto px-3 py-2 ${filter.mainTag.includes(tag) ? "bg-white text-neutral-800" : ""}`}
>
{tag}
</Button>
))}
</div>
)
}

export default TagFilter
37 changes: 29 additions & 8 deletions app/(app)/projects/page.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
import { Projects } from "@/components/pages/projects"
import { Button } from "@/components/ui/button"
import { appMetadata } from "@/config/meteada.config"
import { getAllProjectsAction } from "@/server-actions/projects/projects.actions"
import { mockProjects } from "@/mocks/projects.mocks"
import { Fullscreen } from "lucide-react"
import { Metadata } from "next"

export const dynamic = "force-dynamic"
import Link from "next/link"
import FilterProjects from "./ProjectsFilter"

export const metadata: Metadata = {
...appMetadata.projects,
}

export default async function ProjectsPage() {
const { projects } = await getAllProjectsAction()
// const { projects } = await getAllProjectsAction()
const projectsList = mockProjects(50)

return (
<>
<Projects projects={projects}></Projects>
</>
<div className="relative sm:p-6">
<div className="flex w-full flex-col justify-between gap-2 sm:flex-row sm:items-center">
<h1 className="py-6 pb-12 text-lg sm:text-2xl">
Journey Through the SEI Ecosystem
</h1>
</div>
<div>
<FilterProjects initialProjects={projectsList} />
</div>
<Link href="/ecosystem">
<div className="h-18 w-18 fixed bottom-4 right-4 m-4 bg-white p-0">
<Button
className="z-50 m-0 h-12 w-12 p-0 text-neutral-800"
size="sm"
variant="ghost"
>
<Fullscreen />
</Button>
</div>
</Link>
</div>
)
}
12 changes: 11 additions & 1 deletion app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,21 @@

/* Handle */
::-webkit-scrollbar-thumb {
background: #888;
background: #c5c5c5;
border-radius: 5px;
}

/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #555;
}

/* styles.css */
.scrollbar-left {
direction: rtl; /* Right to left */
overflow-y: scroll; /* Enable vertical scrolling */
}

.scrollbar-left > * {
direction: ltr; /* Reset content to left to right */
}
Binary file modified bun.lockb
Binary file not shown.
6 changes: 5 additions & 1 deletion components/d3/Chart/entities/Canvas/behaviours/zoom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ export const zoomToLevel = function (
export const getZoomLevel = function (
selection: CanvasSelection = select<HTMLCanvasElement, CanvasDatum>("#canvas")
) {
if (!selection || selection.empty()) {
return 30
}

const canvasNode = selection.node()
const canvasDatum = selection.datum()
const zoomScale = canvasDatum.scales.zoom
Expand All @@ -227,7 +231,7 @@ export const getZoomLevel = function (
export const getZoomLevelRange = function (
selection: CanvasSelection = select<HTMLCanvasElement, CanvasDatum>("#canvas")
) {
if (!selection) {
if (!selection || selection.empty()) {
return [30, 30]
}

Expand Down
2 changes: 1 addition & 1 deletion components/d3/Chart/entities/Simulation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export const createSimulation: CreateSimulationFn = function (
1 -
Math.pow(
ALPHA_MIN,
1 / (minimalIterations ? 1 : INITIAL_ALPHA_AIMED_ITERATIONS)
1 / (minimalIterations ? 2 : INITIAL_ALPHA_AIMED_ITERATIONS)
)
)

Expand Down
4 changes: 3 additions & 1 deletion components/d3/Chart/hooks/useRelationChartManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import type { ForceGraphInstance } from "../ForceGraph"
import { CanvasCursorMode, CanvasDatum, NodeDatum } from "../types"
import { isNodeDatum } from "../utils/node"

import { useFilterStore } from "@/stores/project-filter-store"
import { useRelationChartState } from "./useRelationChartState"
import { useSpaceMove } from "./useSpaceMove"

Expand All @@ -87,6 +88,7 @@ export const useRelationChartManager = function () {
setHistory,
setToolTip,
} = useRelationChartState()
const { filteredProjects } = useFilterStore()

const contentRectRef = useRef<DOMRect | null>(null)
const canvasRectRef = useRef<DOMRect | null>(null)
Expand Down Expand Up @@ -277,7 +279,7 @@ export const useRelationChartManager = function () {
[chart, resetDrawTimer]
)
const debouncedOnUpdateDimensions = useMemo(() => {
return debounce(onUpdateDimensions, 10)
return debounce(onUpdateDimensions, 20)
}, [onUpdateDimensions])
const resizeObserver = useRef(
typeof ResizeObserver !== "undefined"
Expand Down
2 changes: 1 addition & 1 deletion components/d3/Chart/hooks/useRelationChartState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const useRelationChartState = create<RelationChartState>()((set) => ({
ready: false,
setReady: (ready) => set(() => ({ ready })),

performanceMode: false,
performanceMode: true,
setPerformanceMode: (enabled: boolean) =>
set(() => ({ performanceMode: enabled })),

Expand Down
15 changes: 9 additions & 6 deletions components/d3/Chart/ui/DetailScreen/DetailScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export type TProps = {
}

export const DetailScreen = ({ nodes }: TProps) => {
const { selection, details, setShowDetails, ready } = useRelationChartState()
const { selection, setSelection, details, setShowDetails, ready } =
useRelationChartState()

const items = React.useMemo(() => nodes, [nodes])

Expand All @@ -24,10 +25,10 @@ export const DetailScreen = ({ nodes }: TProps) => {
return [selectedItems] as const
}, [selection, items])

const onClose = React.useCallback(
() => setShowDetails(false),
[setShowDetails]
)
const onClose = React.useCallback(() => {
setShowDetails(false)
setSelection([])
}, [setShowDetails])

React.useEffect(() => {
if (!selection.length) {
Expand All @@ -43,6 +44,8 @@ export const DetailScreen = ({ nodes }: TProps) => {
}
}, [ready, setShowDetails])

console.log("selection", selection)

return (
<div
className={`relative bg-white transition-all duration-300 ease-out ${details.show ? "max-w-[380px]" : "max-w-0"} relative right-0 z-20 flex h-full flex-1 `}
Expand All @@ -58,7 +61,7 @@ export const DetailScreen = ({ nodes }: TProps) => {
</Button>
{selectedItems.map((item: Project, key) => (
<>
<ProjectCardDetails key={key} item={item} />
<ProjectCardDetails item={item} />
{key !== selectedItems.length - 1 && (
<div className="mb-4 border-b-2 border-gray-300" />
)}
Expand Down
Loading

0 comments on commit 44b2298

Please sign in to comment.