This commit is contained in:
2024-04-02 19:00:29 +05:00
parent d46c90b3d4
commit 1e862b40e2
11 changed files with 219 additions and 110 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
// output: "export",
trailingSlash: true,
distDir: "dist",
};
+36
View File
@@ -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;
+70
View File
@@ -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;
+53
View File
@@ -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
View File
@@ -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>
+20
View File
@@ -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 };
+30
View File
@@ -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;
+1 -1
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
import { IProject } from "../types/IProject";
import LabelCard from "./CardYear";
import LabelCard from "./LabelCart";
import ProjectCard from "./ProjectCard";
type ProjectYearSectionProps = {
+2 -2
View File
@@ -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 };