Skip to content

Commit f3f1924

Browse files
committed
year update, fix search
1 parent 3781e69 commit f3f1924

9 files changed

Lines changed: 110 additions & 29 deletions

File tree

client/src/containers/projects/form/index.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ import CSVImport from "@/components/new-dataset/step-description/csv-import";
6868
import { useGetObjectives } from "@/types/generated/objective";
6969
import { useGetProjectFieldMetadata } from "@/types/generated/project-field-metadata";
7070

71+
const currentYear = new Date().getFullYear();
72+
7173
const ProjectFieldLabel = ({
7274
title,
7375
data,
@@ -242,6 +244,7 @@ export default function ProjectForm() {
242244
status: {
243245
fields: ["name"],
244246
},
247+
year_established: true,
245248
funding: {
246249
fields: ["name"],
247250
},
@@ -370,6 +373,12 @@ export default function ProjectForm() {
370373
status: z.coerce.number().min(1, {
371374
message: "Please enter status",
372375
}),
376+
year_established: z.coerce
377+
.number()
378+
.int()
379+
.min(1900, "Year must be after 1900")
380+
.max(currentYear, "Year cannot be in the future")
381+
.optional(),
373382
funding: z.coerce.number().min(1, {
374383
message: "Please select type of funding",
375384
}),
@@ -410,6 +419,7 @@ export default function ProjectForm() {
410419
previousData?.countries?.data?.map(({ id }: { id?: number }) => id as number) || [],
411420
sdgs: previousData?.sdgs?.data?.map(({ id }: { id?: number }) => id as number) || [],
412421
status: previousData?.status?.data?.id as number,
422+
year_established: (previousData as any)?.year_established as number,
413423
funding: previousData?.funding?.data?.id as number,
414424
organization_type: previousData?.organization_type?.data?.id as number,
415425
source_country: previousData?.source_country?.data?.id as number,
@@ -606,6 +616,7 @@ export default function ProjectForm() {
606616
valueType="project"
607617
values={{
608618
data: {
619+
year_established: "",
609620
name: "",
610621
highlight: "",
611622
status: "",
@@ -841,6 +852,36 @@ export default function ProjectForm() {
841852
);
842853
}}
843854
/>
855+
856+
<FormField
857+
control={form.control}
858+
name="year_established"
859+
render={({ field }) => (
860+
<FormItem className="space-y-1.5">
861+
<ProjectFieldLabel
862+
title="Year established"
863+
data={dataInfo?.data?.attributes?.year_established}
864+
required
865+
/>
866+
<FormControl>
867+
<Input
868+
{...field}
869+
value={field.value}
870+
className={cn({
871+
"border-none bg-gray-300/20 placeholder:text-gray-300/95": true,
872+
"bg-green-400 placeholder:text-gray-400": changes?.includes(field.name),
873+
})}
874+
placeholder="Specify year"
875+
disabled={
876+
ME_DATA?.role?.type === "authenticated" && suggestionStatus === "declined"
877+
}
878+
/>
879+
</FormControl>
880+
881+
<FormMessage />
882+
</FormItem>
883+
)}
884+
/>
844885
<FormField
845886
control={form.control}
846887
name="status"

client/src/containers/projects/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const Projects = () => {
3333
page: 1,
3434
pageSize: 200,
3535
});
36-
console.log(projectSearch);
36+
console.log(projectSearch, projectsData);
3737
return (
3838
<ul className="grid grid-cols-1 space-y-1.5">
3939
{projectsData?.data?.map((p) => {

client/src/containers/projects/popup.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const ProjectPopup = () => {
6060
status: {
6161
fields: ["name", "maturity", "state"],
6262
},
63+
year_established: true,
6364
funding: {
6465
fields: ["name"],
6566
},
@@ -88,6 +89,7 @@ const ProjectPopup = () => {
8889
const sdgs = data?.data?.attributes?.sdgs;
8990
const countries = data?.data?.attributes?.countries;
9091
const projectStatus = data?.data?.attributes?.status?.data?.attributes;
92+
const yearEstablished = data?.data?.attributes?.year_established;
9193
const projectTypeOfFunding = data?.data?.attributes?.funding?.data?.attributes?.name;
9294
const projectOtherFunding = data?.data?.attributes?.other_funding;
9395
const organizationType = data?.data?.attributes?.organization_type?.data?.attributes?.name;
@@ -116,7 +118,19 @@ const ProjectPopup = () => {
116118
<div className="divide-y divide-gray-200 px-10">
117119
{/* STATUS */}
118120
<div className="py-5">
121+
{/* STATUS - progress bar*/}
119122
<ProjectsStatusProgressBar {...projectStatus} />
123+
{/* STATUS - year established*/}
124+
{!yearEstablished && (
125+
<div className="space-y-3.5">
126+
<div className="flex items-center space-x-1">
127+
<span className="text-xxs font-semibold uppercase tracking-wide text-gray-500">
128+
Year established:
129+
</span>
130+
<span className="text-xs">{yearEstablished || "Not provided"}</span>
131+
</div>
132+
</div>
133+
)}
120134
</div>
121135
<section className="space-y-5 py-5">
122136
{/* HIGHLIGHT */}

client/src/types/generated/strapi.schemas.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3219,6 +3219,7 @@ export type SdgProjectsDataItemAttributes = {
32193219
updatedAt?: string;
32203220
updatedBy?: SdgProjectsDataItemAttributesUpdatedBy;
32213221
video_link?: string;
3222+
year_established?: number;
32223223
};
32233224

32243225
export type SdgProjectsDataItem = {
@@ -5349,6 +5350,7 @@ export interface ProjectFieldMetadata {
53495350
updatedAt?: string;
53505351
updatedBy?: ProjectFieldMetadataUpdatedBy;
53515352
video_link?: string;
5353+
year_established?: string;
53525354
}
53535355

53545356
export type ProjectFieldMetadataCreatedByDataAttributesUpdatedByDataAttributes = {
@@ -5555,6 +5557,7 @@ export type ProjectFieldMetadataRequestData = {
55555557
source_country?: string;
55565558
status?: string;
55575559
video_link?: string;
5560+
year_established?: string;
55585561
};
55595562

55605563
export interface ProjectFieldMetadataRequest {
@@ -5721,6 +5724,7 @@ export type ProjectEditSuggestionPillarDataAttributesProjectsDataItemAttributes
57215724
updatedAt?: string;
57225725
updatedBy?: ProjectEditSuggestionPillarDataAttributesProjectsDataItemAttributesUpdatedBy;
57235726
video_link?: string;
5727+
year_established?: number;
57245728
};
57255729

57265730
export type ProjectEditSuggestionPillarDataAttributesProjectsDataItem = {
@@ -7391,6 +7395,7 @@ export interface Project {
73917395
updatedAt?: string;
73927396
updatedBy?: ProjectUpdatedBy;
73937397
video_link?: string;
7398+
year_established?: number;
73947399
}
73957400

73967401
export interface ProjectResponseDataObject {
@@ -7507,6 +7512,7 @@ export type ProjectPillarDataAttributesProjectsDataItemAttributes = {
75077512
updatedAt?: string;
75087513
updatedBy?: ProjectPillarDataAttributesProjectsDataItemAttributesUpdatedBy;
75097514
video_link?: string;
7515+
year_established?: number;
75107516
};
75117517

75127518
export type ProjectPillarDataAttributesProjectsDataItem = {
@@ -9128,6 +9134,7 @@ export type ProjectRequestData = {
91289134
source_country?: ProjectRequestDataSourceCountry;
91299135
status?: ProjectRequestDataStatus;
91309136
video_link?: string;
9137+
year_established?: number;
91319138
};
91329139

91339140
export type PillarResponseMeta = { [key: string]: any };
@@ -9211,6 +9218,7 @@ export type PillarProjectsDataItemAttributes = {
92119218
updatedAt?: string;
92129219
updatedBy?: PillarProjectsDataItemAttributesUpdatedBy;
92139220
video_link?: string;
9221+
year_established?: number;
92149222
};
92159223

92169224
export type PillarProjectsDataItemAttributesStatusDataAttributes = { [key: string]: any };

cms/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
"strapi-plugin-config-sync": "^1.2.1",
3131
"strapi-plugin-import-export-entries": "^1.21.1"
3232
},
33+
"devDependencies": {
34+
"@types/csv-parse": "^1.2.2",
35+
"@types/csv-stringify": "^3.1.0"
36+
},
3337
"packageManager": "yarn@3.6.4",
3438
"engines": {
3539
"node": ">=16.0.0 <=20.x.x",
@@ -38,9 +42,5 @@
3842
"strapi": {
3943
"uuid": "d6c35ecd-a7d3-4e06-bbec-080329e0e5b8",
4044
"telemetryDisabled": true
41-
},
42-
"devDependencies": {
43-
"@types/csv-parse": "^1.2.2",
44-
"@types/csv-stringify": "^3.1.0"
4545
}
4646
}

cms/src/api/project-status/content-types/project-status/schema.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
"type": "string"
1717
},
1818
"maturity": {
19+
"type": "string"
20+
},
21+
"state": {
1922
"type": "integer"
2023
}
2124
}

cms/src/api/project/content-types/project/schema.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@
8181
},
8282
"video_link": {
8383
"type": "string"
84+
},
85+
"year_established": {
86+
"type": "integer"
8487
}
8588
}
8689
}

cms/src/api/project/controllers/project.ts

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,15 @@ export default factories.createCoreController("api::project.project", () => ({
133133
pillar: { select: ["id", "name"] },
134134
sdgs: { select: ["id", "name"] },
135135
countries: { select: ["id", "name", "iso3"] },
136-
status: { select: ["maturity", "name"] },
136+
status: { select: ["maturity", "name", "state"] },
137137
};
138138

139139
// --- Sorting ---
140140
// Note: status.maturity lives in populated relation; Strapi can sort by relation fields in many cases.
141141
// If it doesn't in your setup, we will sort in JS as fallback (below).
142142
const sort =
143143
sortField === "status"
144-
? [{ status: { maturity: sortOrder } }]
144+
? [{ status: { state: sortOrder } }]
145145
: [{ name: sortOrder }];
146146

147147
// --- Case: empty q OR q too short -> return all with filters/sort/pagination ---
@@ -175,24 +175,28 @@ export default factories.createCoreController("api::project.project", () => ({
175175
}
176176

177177
// --- Search flow ---
178-
// 1) Pre-filter in DB using containsi on name/highlight (same as GET_PROJECTS_OPTIONS)
179-
// to reduce candidates
178+
const qTrim = (q ?? "").trim();
179+
if (!qTrim)
180+
return ctx.send({
181+
data: [],
182+
meta: { pagination: { page, pageSize, pageCount: 0, total: 0 } },
183+
});
184+
185+
const qLower = qTrim.toLowerCase();
186+
187+
// Get candidates with your base filters only (or keep your DB prefilter if you want)
180188
const candidates = await strapi.db
181189
.query("api::project.project")
182190
.findMany({
183-
where: {
184-
...where,
185-
$or: [
186-
{ name: { $containsi: q } },
187-
{ highlight: { $containsi: q } },
188-
],
189-
},
191+
where,
192+
// make sure highlight is actually included:
193+
select: ["id", "name", "highlight"],
190194
populate,
191-
limit: 2000, // adjust if needed
195+
limit: 2000,
192196
});
193197

194-
// 2) Whole-word filter on NAME only (your requirements)
195-
const rawTokens = q
198+
// 3) Whole-word filter on NAME only (your requirement)
199+
const rawTokens = qTrim
196200
.split(/\s+/)
197201
.map((t) => t.trim())
198202
.filter(Boolean);
@@ -201,16 +205,22 @@ export default factories.createCoreController("api::project.project", () => ({
201205
);
202206
const wordRes = tokens.map((t) => new RegExp(`(^|\\W)${t}($|\\W)`, "i"));
203207

204-
const filtered = candidates.filter((p: any) => {
205-
const name = String(p.name ?? "");
206-
return wordRes.every((re) => re.test(name));
208+
const filtered = candidates.filter((p) => {
209+
const name = String(p?.name ?? "");
210+
const highlight = String(p?.highlight ?? "");
211+
212+
const matchesHighlight = highlight.toLowerCase().includes(qLower);
213+
const matchesNameWholeWord = wordRes.every((re) => re.test(name));
214+
215+
// ✅ keep if either matches
216+
return matchesHighlight || matchesNameWholeWord;
207217
});
208218

209-
// 3) Sort fallback in JS (reliable)
210-
const sorted = filtered.sort((a: any, b: any) => {
219+
// 4) Sort fallback in JS (reliable)
220+
const sorted = filtered.sort((a, b) => {
211221
if (sortField === "status") {
212-
const av = a?.status?.maturity ?? Number.NEGATIVE_INFINITY;
213-
const bv = b?.status?.maturity ?? Number.NEGATIVE_INFINITY;
222+
const av = a?.status?.state ?? Number.NEGATIVE_INFINITY;
223+
const bv = b?.status?.state ?? Number.NEGATIVE_INFINITY;
214224
return sortOrder === "asc" ? av - bv : bv - av;
215225
}
216226
const av = String(a?.name ?? "");
@@ -220,13 +230,13 @@ export default factories.createCoreController("api::project.project", () => ({
220230
: bv.localeCompare(av);
221231
});
222232

223-
// 4) Pagination
233+
// 5) Pagination
224234
const total = sorted.length;
225235
const pageCount = Math.ceil(total / pageSize);
226236
const pageItems = sorted.slice(offset, offset + pageSize);
227237

228238
return ctx.send({
229-
data: pageItems.map((p: any) => {
239+
data: pageItems.map((p) => {
230240
const { id, ...attributes } = p;
231241
return { id, attributes };
232242
}),

cms/types/generated/contentTypes.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,7 @@ export interface ApiProjectProject extends Schema.CollectionType {
13451345
>;
13461346
other_funding: Attribute.String;
13471347
video_link: Attribute.String;
1348+
year_established: Attribute.Integer;
13481349
createdAt: Attribute.DateTime;
13491350
updatedAt: Attribute.DateTime;
13501351
publishedAt: Attribute.DateTime;
@@ -1515,7 +1516,8 @@ export interface ApiProjectStatusProjectStatus extends Schema.CollectionType {
15151516
};
15161517
attributes: {
15171518
name: Attribute.String;
1518-
maturity: Attribute.Integer;
1519+
maturity: Attribute.String;
1520+
state: Attribute.Integer;
15191521
createdAt: Attribute.DateTime;
15201522
updatedAt: Attribute.DateTime;
15211523
publishedAt: Attribute.DateTime;

0 commit comments

Comments
 (0)