projects
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: "export",
|
||||
// output: "export",
|
||||
trailingSlash: true,
|
||||
distDir: "dist",
|
||||
};
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Fragment } from "react";
|
||||
import ProjectsSection from "@components/ProjectsSection";
|
||||
import { IProject } from "../types/IProject";
|
||||
import { SortedProject } from "../types/SortedProject";
|
||||
|
||||
interface IProjectsProps {
|
||||
projects: IProject[];
|
||||
projectLabels: (string | number)[];
|
||||
sortedProjects: SortedProject | undefined;
|
||||
}
|
||||
|
||||
const DesktopProjects = ({
|
||||
projects,
|
||||
projectLabels,
|
||||
sortedProjects,
|
||||
}: IProjectsProps) => {
|
||||
return (
|
||||
<div className="flex-col gap-8 2xl:flex hidden">
|
||||
{projectLabels.map((year) => {
|
||||
const projects = (sortedProjects && sortedProjects.get(year)) ?? [];
|
||||
|
||||
return (
|
||||
<Fragment key={year}>
|
||||
{projects.length !== 0 && (
|
||||
<div className="border-t border-t-[#3D425C]">
|
||||
<ProjectsSection year={year} projects={projects} />
|
||||
</div>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DesktopProjects;
|
||||
@@ -0,0 +1,70 @@
|
||||
import { Fragment, useEffect, useLayoutEffect, useState } from "react";
|
||||
import MoreProjectButton from "@components/MoreProjectButton";
|
||||
import ProjectCard from "@components/ProjectCard";
|
||||
import { IProject } from "../types/IProject";
|
||||
import { SortedProject } from "../types/SortedProject";
|
||||
import LabelCard from "@components/LabelCart";
|
||||
import { getTime, getYear } from "date-fns";
|
||||
|
||||
interface IProjectsProps {
|
||||
projects: IProject[];
|
||||
projectLabels: (string | number)[];
|
||||
sortedProjects: SortedProject | undefined;
|
||||
}
|
||||
|
||||
const MobileProjects = ({ sortedProjects }: IProjectsProps) => {
|
||||
const [currentCount, setCurrentCount] = useState(5);
|
||||
const [projects, setProjects] = useState<IProject[]>([]);
|
||||
let previosLabel: string | number = "";
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!sortedProjects) return;
|
||||
|
||||
const _projects = [];
|
||||
const values = Array.from(sortedProjects.values());
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
const pr = values[i];
|
||||
for (let j = 0; j < pr.length; j++) {
|
||||
_projects.push(pr[j]);
|
||||
}
|
||||
}
|
||||
|
||||
setProjects(_projects);
|
||||
}, [sortedProjects]);
|
||||
|
||||
const handleShowMore = () => {
|
||||
setCurrentCount((prev) => prev + 8);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="2xl:hidden grid xl:grid-cols-4 sm:grid-cols-3 gap-4 ">
|
||||
{projects.slice(0, currentCount).map((project, index) => {
|
||||
const projectYear = getYear(project.releaseDate);
|
||||
const label = project.stage !== 6 ? "В работе" : projectYear;
|
||||
const projects = sortedProjects?.get(projectYear);
|
||||
if (previosLabel === "") {
|
||||
previosLabel = label;
|
||||
}
|
||||
|
||||
if ((index === 0 || previosLabel !== label) && projects?.length !== 0) {
|
||||
previosLabel = label;
|
||||
return (
|
||||
<>
|
||||
{/* <div className="basis-full h-0 bg-black w-full"></div> */}
|
||||
<LabelCard key={project.id} label={label} />
|
||||
<ProjectCard key={project.id} {...project} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return <ProjectCard key={project.id} {...project} />;
|
||||
})}
|
||||
|
||||
{projects.length > currentCount && (
|
||||
<MoreProjectButton onClick={handleShowMore} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileProjects;
|
||||
@@ -0,0 +1,53 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import Heading2 from "../components/Headings/Heading2";
|
||||
import { IProject } from "../types/IProject";
|
||||
import { SortedProject } from "../types/SortedProject";
|
||||
import DesktopProjects from "./DesktopProjects";
|
||||
import MobileProjects from "./MobileProjects";
|
||||
import api from "@utils/api";
|
||||
import { getProjectLabels } from "../calc/getProjectLabels";
|
||||
import { getSortedProjects } from "../calc/getSortedProjects";
|
||||
|
||||
const Projects = () => {
|
||||
const [projects, setProjects] = useState<IProject[]>([]);
|
||||
const [projectLabels, setProjectLabels] = useState<(string | number)[]>([]);
|
||||
const [sortedProjects, setSortedProjects] = useState<SortedProject>();
|
||||
|
||||
async function getProjects() {
|
||||
try {
|
||||
const _projects: IProject[] = await api.get("projects").json();
|
||||
const _projectLabels = getProjectLabels(_projects);
|
||||
const _sortedProjects = getSortedProjects(_projects);
|
||||
|
||||
setProjectLabels(_projectLabels);
|
||||
setSortedProjects(_sortedProjects);
|
||||
setProjects(_projects);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getProjects();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col 2xl:gap-16 xl:gap-10 gap-8 2xl:mb-[200px] sm:mb-[120px] mb-20">
|
||||
<Heading2>Проекты</Heading2>
|
||||
<DesktopProjects
|
||||
projects={projects}
|
||||
projectLabels={projectLabels}
|
||||
sortedProjects={sortedProjects}
|
||||
/>
|
||||
<MobileProjects
|
||||
projects={projects}
|
||||
projectLabels={projectLabels}
|
||||
sortedProjects={sortedProjects}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Projects;
|
||||
+4
-104
@@ -35,6 +35,7 @@ import { motion } from "framer-motion";
|
||||
import { Video } from "../types/Video";
|
||||
import Reviews from "@components/Reviews";
|
||||
import Histories from "@components/Histories";
|
||||
import Projects from "../Projects/Projects";
|
||||
|
||||
const VIDEOS_FEATURES: Video[] = [
|
||||
{
|
||||
@@ -99,56 +100,13 @@ const VIDEOS_FEATURES: Video[] = [
|
||||
},
|
||||
];
|
||||
|
||||
function getSortedProjects(projects: IProject[]) {
|
||||
const sorted = [...projects].sort(
|
||||
(da, db) => getTime(db.releaseDate) - getTime(da.releaseDate)
|
||||
);
|
||||
|
||||
const sortedProject: SortedProject = new Map();
|
||||
|
||||
for (let i = 0; i < sorted.length; i++) {
|
||||
const project = sorted[i];
|
||||
const projectYear = getYear(project.releaseDate);
|
||||
const key = project.stage !== 6 ? "В работе" : projectYear;
|
||||
|
||||
if (sortedProject.has(key)) {
|
||||
const prevProjects = sortedProject.get(key) as IProject[];
|
||||
const updatedProjects = [...prevProjects, project];
|
||||
sortedProject.set(key, updatedProjects);
|
||||
} else {
|
||||
const createdProjects = [project];
|
||||
sortedProject.set(key, createdProjects);
|
||||
}
|
||||
}
|
||||
|
||||
return sortedProject;
|
||||
}
|
||||
|
||||
function getProjectLabels(projects: IProject[]) {
|
||||
const projectYears: number[] = [];
|
||||
|
||||
for (let i = 0; i < projects.length; i++) {
|
||||
const project = getYear(projects[i].releaseDate);
|
||||
if (projectYears.includes(project)) continue;
|
||||
projectYears.push(project);
|
||||
}
|
||||
|
||||
projectYears.sort((a, b) => b - a);
|
||||
|
||||
const projectLabels = ["В работе", ...projectYears];
|
||||
|
||||
return projectLabels;
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [selectedVideo, setSelectedVideo] = useState<string>(
|
||||
"/videos/features/virtual_tour.mp4"
|
||||
);
|
||||
const [projects, setProjects] = useState<IProject[]>([]);
|
||||
const [projectLabels, setProjectLabels] = useState<(string | number)[]>([]);
|
||||
const [sortedProjects, setSortedProjects] = useState<SortedProject>();
|
||||
|
||||
const [setModal] = useModalStore((state) => [state.setModal]);
|
||||
const [isShownAllProjects, setIsShownAllProjects] = useState<boolean>(false);
|
||||
|
||||
const [isBuffering, setIsBuffering] = useState(true);
|
||||
const [isViewportEntered, setIsViewportEntered] = useState(false);
|
||||
|
||||
@@ -158,26 +116,6 @@ export default function App() {
|
||||
setIsViewportEntered(true);
|
||||
};
|
||||
|
||||
async function getProjects() {
|
||||
try {
|
||||
const _projects: IProject[] = await api.get("projects").json();
|
||||
const _projectLabels = getProjectLabels(_projects);
|
||||
const _sortedProjects = getSortedProjects(_projects);
|
||||
|
||||
setProjectLabels(_projectLabels);
|
||||
setSortedProjects(_sortedProjects);
|
||||
setProjects(_projects);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
alert(`Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getProjects();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="min-h-screen 2xl:px-10 xl:px-8 sm:px-6 px-4 overflow-x-clip">
|
||||
@@ -674,45 +612,7 @@ export default function App() {
|
||||
|
||||
<Winners />
|
||||
<Reviews />
|
||||
|
||||
<div className="flex flex-col 2xl:gap-16 xl:gap-10 gap-8 2xl:mb-[200px] sm:mb-[120px] mb-20">
|
||||
<Heading2>Проекты</Heading2>
|
||||
<div className="flex-col gap-8 2xl:flex hidden">
|
||||
{projectLabels.map((year) => {
|
||||
const projects =
|
||||
(sortedProjects && sortedProjects.get(year)) ?? [];
|
||||
|
||||
return (
|
||||
<Fragment key={year}>
|
||||
{projects.length !== 0 && (
|
||||
<div className="border-t border-t-[#3D425C]">
|
||||
<ProjectsSection year={year} projects={projects} />
|
||||
</div>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="2xl:hidden grid xl:grid-cols-4 sm:grid-cols-3 gap-4">
|
||||
{[...Array.from({ length: 5 })].map((_, index) => (
|
||||
<ProjectCard key={index} {...projects[index]} />
|
||||
))}
|
||||
{!isShownAllProjects ? (
|
||||
<MoreProjectButton
|
||||
onClick={() => setIsShownAllProjects(true)}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{projects.map(
|
||||
(_, index) =>
|
||||
projects[index + 5] && (
|
||||
<ProjectCard key={index} {...projects[index + 5]} />
|
||||
)
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Projects />
|
||||
|
||||
<div className="relative flex flex-col 2xl:gap-16 xl:gap-10 gap-8 2xl:mb-[200px] sm:mb-[120px] mb-20">
|
||||
<Heading2>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { getYear } from "date-fns";
|
||||
import { IProject } from "../types/IProject";
|
||||
|
||||
function getProjectLabels(projects: IProject[]) {
|
||||
const projectYears: number[] = [];
|
||||
|
||||
for (let i = 0; i < projects.length; i++) {
|
||||
const year = getYear(projects[i].releaseDate);
|
||||
if (projectYears.includes(year)) continue;
|
||||
projectYears.push(year);
|
||||
}
|
||||
|
||||
projectYears.sort((a, b) => b - a);
|
||||
|
||||
const projectLabels = ["В работе", ...projectYears];
|
||||
|
||||
return projectLabels;
|
||||
}
|
||||
|
||||
export { getProjectLabels };
|
||||
@@ -0,0 +1,30 @@
|
||||
import { getTime, getYear } from "date-fns";
|
||||
import { IProject } from "../types/IProject";
|
||||
import { SortedProject } from "../types/SortedProject";
|
||||
|
||||
function getSortedProjects(projects: IProject[]) {
|
||||
const sorted = [...projects].sort(
|
||||
(da, db) => getTime(db.releaseDate) - getTime(da.releaseDate)
|
||||
);
|
||||
|
||||
const sortedProject: SortedProject = new Map();
|
||||
|
||||
for (let i = 0; i < sorted.length; i++) {
|
||||
const project = sorted[i];
|
||||
const projectYear = getYear(project.releaseDate);
|
||||
const key = project.stage !== 6 ? "В работе" : projectYear;
|
||||
|
||||
if (sortedProject.has(key)) {
|
||||
const prevProjects = sortedProject.get(key) as IProject[];
|
||||
const updatedProjects = [...prevProjects, project];
|
||||
sortedProject.set(key, updatedProjects);
|
||||
} else {
|
||||
const createdProjects = [project];
|
||||
sortedProject.set(key, createdProjects);
|
||||
}
|
||||
}
|
||||
|
||||
return sortedProject;
|
||||
}
|
||||
|
||||
export { getSortedProjects };
|
||||
@@ -3,7 +3,7 @@ interface LabelCardProps {
|
||||
}
|
||||
|
||||
const LabelCard = ({ label }: LabelCardProps): JSX.Element => {
|
||||
return <div className="font-semibold text-xl">{label}</div>;
|
||||
return <div className="font-semibold text-xl aspect-square">{label}</div>;
|
||||
};
|
||||
|
||||
export default LabelCard;
|
||||
@@ -13,7 +13,7 @@ function MoreProjectButton({ onClick }: IMoreProjectButtonProps) {
|
||||
whileInView={{ opacity: 1 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 1, ease: [0.58, 0.12, 0.27, 0.98], delay: 0.2 }}
|
||||
className="border border-[#3D425C] p-4 flex sm:flex-col sm:justify-end items-end gap-2 sm:rounded-none rounded-full"
|
||||
className="border border-[#3D425C] p-4 flex sm:flex-col sm:justify-end items-end gap-2 sm:rounded-none rounded-full aspect-square"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="flex gap-2 items-center justify-between sm:justify-end w-full">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IProject } from "../types/IProject";
|
||||
import LabelCard from "./CardYear";
|
||||
import LabelCard from "./LabelCart";
|
||||
import ProjectCard from "./ProjectCard";
|
||||
|
||||
type ProjectYearSectionProps = {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Device from "./Device";
|
||||
|
||||
interface IProject {
|
||||
id?: string;
|
||||
id: string;
|
||||
name: string;
|
||||
company: string;
|
||||
city: string;
|
||||
@@ -11,4 +11,4 @@ interface IProject {
|
||||
devices: Device[];
|
||||
}
|
||||
|
||||
export type {IProject};
|
||||
export type { IProject };
|
||||
|
||||
Reference in New Issue
Block a user