Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions apps/api/prisma/migrations/20251010150458_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
-- CreateTable
CREATE TABLE "QueryCount" (
"id" INTEGER NOT NULL DEFAULT 1,
"total_queries" BIGINT NOT NULL,

CONSTRAINT "QueryCount_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"firstName" TEXT NOT NULL,
"authMethod" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"lastLogin" TIMESTAMP(3) NOT NULL,

CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "Account" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"type" TEXT NOT NULL,
"provider" TEXT NOT NULL,
"providerAccountId" TEXT NOT NULL,
"refresh_token" TEXT,
"access_token" TEXT,
"expires_at" INTEGER,
"token_type" TEXT,
"scope" TEXT,
"id_token" TEXT,
"session_state" TEXT,

CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

-- CreateIndex
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");

-- AddForeignKey
ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
3 changes: 3 additions & 0 deletions apps/api/prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"
7 changes: 2 additions & 5 deletions apps/api/src/routers/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { projectService } from "../services/project.service.js";
import { queryService } from "../services/query.service.js";

const filterPropsSchema = z.object({
language: z.string().optional(),
language: z.array(z.string()).optional(),
stars: z
.object({
min: z.string().optional(),
Expand Down Expand Up @@ -40,9 +40,6 @@ export const projectRouter = router({
.input(inputSchema)
.query(async ({ input, ctx }: any): Promise<RepositoryProps[]> => {
await queryService.incrementQueryCount(ctx.db.prisma);
return await projectService.fetchGithubProjects(
input.filters as any,
input.options as any
);
return await projectService.fetchGithubProjects(input.filters as any, input.options as any);
}),
});
7 changes: 1 addition & 6 deletions apps/api/src/services/project.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { graphql } from "@octokit/graphql";
import dotenv from "dotenv";
import type {
FilterProps,
RepositoryProps,
GraphQLResponseProps,
OptionsTypesProps,
} from "@opensox/shared";
import type { FilterProps, RepositoryProps, GraphQLResponseProps, OptionsTypesProps } from "@opensox/shared";

dotenv.config();

Expand Down
82 changes: 51 additions & 31 deletions apps/web/src/components/ui/Filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,63 +5,83 @@ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Label } from "@/components/ui/label";
import { useFilterInputStore } from "@/store/useFilterInputStore";
import clsx from "clsx";
import { useState } from "react";
import { Checkbox } from "./checkbox";

export default function Filter({
filterName,
filters,
onClick,
multiple = false,
}: {
filterName: string;
filters: string[];
onClick?: () => void;
multiple?: boolean;
}) {
const { updateFilters } = useFilterInputStore();
const inputData: { [key: string]: string } = {};
const [selected, setSelected] = useState<string | string[]>(multiple ? [] : "");

const recordFilterInput = (filter: string) => {
inputData[filterName] = filter;
updateFilters(inputData);
let updatedValue: string | string[];

if (multiple) {
// Toggle checkbox selections
const current = selected as string[];
updatedValue = current.includes(filter) ? current.filter((f) => f !== filter) : [...current, filter];
} else {
// Single choice (radio)
updatedValue = filter;
}

setSelected(updatedValue);
updateFilters({ [filterName]: updatedValue });
};

const triggerClasses = clsx("text-sm font-medium", {
"text-slate-500": ["Hire contributors", "Funding", "Trending"].includes(
filterName
),
"text-slate-500": ["Hire contributors", "Funding", "Trending"].includes(filterName),
});

return (
<div onClick={onClick}>
<AccordionItem value={filterName} className="px-3">
<AccordionTrigger className={triggerClasses}>
{filterName}
</AccordionTrigger>
<AccordionTrigger className={triggerClasses}>{filterName}</AccordionTrigger>
<AccordionContent
className={
filterName === "Hire contributors" ||
filterName === "Funding" ||
filterName === "Trending"
filterName === "Hire contributors" || filterName === "Funding" || filterName === "Trending"
? "text-white-100"
: ""
}
>
<RadioGroup>
{filters.map((filter) => (
<div key={filter} className="flex items-center space-x-2">
<RadioGroupItem
value={filter}
id={filter}
onClick={() => {
recordFilterInput(filter);
}}
/>
<Label
htmlFor={filter}
onClick={() => recordFilterInput(filter)}
>
{filter}
</Label>
</div>
))}
</RadioGroup>
{multiple ? (
// Multiple-choice (checkbox)
<div className="space-y-2">
{filters.map((filter) => (
<div key={filter} className="flex items-center space-x-2 cursor-pointer">
<Checkbox
id={filter}
checked={(selected as string[]).includes(filter)}
onCheckedChange={() => recordFilterInput(filter)}
/>
<Label htmlFor={filter} className="cursor-pointer select-none">
{filter}
</Label>
</div>
))}
</div>
) : (
// Single-choice (radio)
<RadioGroup value={selected as string}>
{filters.map((filter) => (
<div key={filter} className="flex items-center space-x-2">
<RadioGroupItem value={filter} id={filter} onClick={() => recordFilterInput(filter)} />
<Label htmlFor={filter} onClick={() => recordFilterInput(filter)}>
{filter}
</Label>
</div>
))}
</RadioGroup>
)}
</AccordionContent>
</AccordionItem>
</div>
Expand Down
54 changes: 11 additions & 43 deletions apps/web/src/components/ui/FiltersContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import Filter from "./Filter";
import { useFilterStore } from "@/store/useFilterStore";
import { useFilterInputStore } from "@/store/useFilterInputStore";
import { useGetProjects } from "@/hooks/useGetProjects";
import {
convertUserInputToApiInput,
convertApiOutputToUserOutput,
} from "@/utils/converter";
import { convertUserInputToApiInput, convertApiOutputToUserOutput } from "@/utils/converter";
import { useRouter } from "next/navigation";
import { useRenderProjects } from "@/store/useRenderProjectsStore";
import { useProjectsData } from "@/store/useProjectsDataStore";
Expand Down Expand Up @@ -58,18 +55,12 @@ export default function FiltersContainer() {

return (
<div className="fixed top-8 md:top-14 right-4 md:right-2 inset-0 z-50 flex justify-center items-start p-4 lg:p-0">
<div
className="fixed inset-0 bg-black/20"
onClick={() => toggleShowFilters()}
/>
<div className="fixed inset-0 bg-black/20" onClick={() => toggleShowFilters()} />
<div className="w-full max-w-[90%] md:max-w-[400px] lg:w-80 h-[60vh] lg:h-96 flex flex-col rounded-lg border border-ox-gray bg-ox-black-1 relative lg:absolute lg:right-20">
<div className="flex h-8 lg:h-[10%] border-b border-ox-gray justify-between items-center px-4">
<h1 className="text-sm text-ox-purple font-semibold">Filters</h1>
<IconWrapper>
<XMarkIcon
className="size-5 lg:size-4 text-ox-purple"
onClick={() => toggleShowFilters()}
/>
<XMarkIcon className="size-5 lg:size-4 text-ox-purple" onClick={() => toggleShowFilters()} />
</IconWrapper>
</div>

Expand All @@ -95,38 +86,15 @@ export default function FiltersContainer() {
"Html",
"Elixir",
]}
multiple={true}
/>
<Filter
filterName="Popularity"
filters={["Very low", "Low", "Moderate", "High", "Very high"]}
/>
<Filter
filterName="Competition"
filters={["Very low", "Low", "Moderate", "High", "Very high"]}
/>
<Filter
filterName="Stage"
filters={["Very early", "Early", "Emerging", "Established"]}
/>
<Filter
filterName="Activity"
filters={["Highest", "High", "Normal", "Low"]}
/>
<Filter
filterName="Hire contributors"
filters={[]}
onClick={handleClickWipFilters}
/>
<Filter
filterName="Funding"
filters={[]}
onClick={handleClickWipFilters}
/>
<Filter
filterName="Trending"
filters={[]}
onClick={handleClickWipFilters}
/>
<Filter filterName="Popularity" filters={["Very low", "Low", "Moderate", "High", "Very high"]} />
<Filter filterName="Competition" filters={["Very low", "Low", "Moderate", "High", "Very high"]} />
<Filter filterName="Stage" filters={["Very early", "Early", "Emerging", "Established"]} />
<Filter filterName="Activity" filters={["Highest", "High", "Normal", "Low"]} />
<Filter filterName="Hire contributors" filters={[]} onClick={handleClickWipFilters} />
<Filter filterName="Funding" filters={[]} onClick={handleClickWipFilters} />
<Filter filterName="Trending" filters={[]} onClick={handleClickWipFilters} />
</Accordion>
</div>

Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/store/useFilterInputStore.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { create } from "zustand";

interface FilterInputState {
filters: object;
updateFilters: (newFilter: Record<string, string>) => void;
filters: Record<string, string | string[]>;
updateFilters: (newFilter: Record<string, string | string[]>) => void;
resetFilters: () => void;
}

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/types/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export type ProjectProps = {
};

export type UserInputFilterProps = {
"Tech stack"?: string;
"Tech stack"?: string[];
Popularity?: string;
Competition?: string;
Stage?: string;
Expand Down
32 changes: 16 additions & 16 deletions packages/shared/types/filter.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
export type DateRange = {
start: string;
end?: string;
}
start: string;
end?: string;
};

export type StarRange = {
min?: string;
max?: string;
custom?: string;
}
min?: string;
max?: string;
custom?: string;
};

export type ForkRange = {
min?: string;
max?: string;
}
min?: string;
max?: string;
};

export type FilterProps = {
language?: string;
stars?: StarRange;
forks?: ForkRange;
pushed?: string;
created?: string;
}
language?: string[];
stars?: StarRange;
forks?: ForkRange;
pushed?: string;
created?: string;
};