diff --git a/app/student/register/steps/RegisterStep.tsx b/app/student/register/steps/RegisterStep.tsx
index 157ea516..39f7e70f 100644
--- a/app/student/register/steps/RegisterStep.tsx
+++ b/app/student/register/steps/RegisterStep.tsx
@@ -5,7 +5,7 @@ import { UseFormReturn } from "react-hook-form";
import { FormInputs } from "../page";
import { Autocomplete } from "@/components/ui/autocomplete";
import { DEGREES } from "./tempDegrees";
-import { sortUniversityOptions } from "../../../../lib/student-forms-access";
+import { sortUniversityOptions, universityAcronyms } from "../../../../lib/student-forms-access";
import {
Accordion,
AccordionContent,
@@ -38,7 +38,13 @@ export function RegisterStep({
const hasValidUniversity = refs.universities.some(
(option) => option.id === university,
);
- const universityOptions = sortUniversityOptions(refs.universities);
+ const universityOptions = sortUniversityOptions(refs.universities).map(
+ (u) => ({
+ id: u.id,
+ name: u.name,
+ keywords: universityAcronyms(u.name),
+ }),
+ );
const canCreateAccount =
firstName.trim() && lastName.trim() && hasValidUniversity && degree.trim();
@@ -78,7 +84,7 @@ export function RegisterStep({
setter={(val) => {
regForm.setValue("university", val === null ? "" : String(val));
}}
- options={universityOptions as { id: string; name: string }[]}
+ options={universityOptions}
value={university}
required={true}
preserveOptionOrder={true}
diff --git a/components/features/hire/paused-listings-banner.tsx b/components/features/hire/paused-listings-banner.tsx
index f1193d99..b202f724 100644
--- a/components/features/hire/paused-listings-banner.tsx
+++ b/components/features/hire/paused-listings-banner.tsx
@@ -47,9 +47,7 @@ export function PausedListingsBanner({
-
- {pausedCount} of your listing{pausedCount !== 1 ? "s" : ""}
- {" "}
+ {pausedCount} of your listings{" "}
{pausedCount !== 1 ? "are" : "is"} marked inactive
{waitingTotal > 0 && (
<>
diff --git a/components/features/student/profile/ProfileEditor.tsx b/components/features/student/profile/ProfileEditor.tsx
index e91564b5..b3aed3a9 100644
--- a/components/features/student/profile/ProfileEditor.tsx
+++ b/components/features/student/profile/ProfileEditor.tsx
@@ -39,7 +39,7 @@ import {
isValidOptionalURL,
toURL,
} from "@/lib/utils/url-utils";
-import { sortUniversityOptions } from "@/lib/student-forms-access";
+import { sortUniversityOptions, universityAcronyms } from "@/lib/student-forms-access";
import { DEGREES } from "@/app/student/register/steps/tempDegrees";
import { ResumeSection } from "./ResumeSection";
import type { ProfileResumeManager, ProfileSectionKey } from "./profile-types";
@@ -485,7 +485,13 @@ export const ProfileEditor = forwardRef<
({
+ id: u.id,
+ name: u.name,
+ keywords: universityAcronyms(u.name),
+ }),
+ )}
value={formData.university}
setter={fieldSetter("university")}
placeholder="Select University"
diff --git a/components/ui/autocomplete.tsx b/components/ui/autocomplete.tsx
index 1dc92583..686ba7bd 100644
--- a/components/ui/autocomplete.tsx
+++ b/components/ui/autocomplete.tsx
@@ -12,6 +12,7 @@ import { useAppContext } from "@/lib/ctx-app";
export interface IAutocompleteOption {
id: ID;
name: string;
+ keywords?: string[];
}
type MobileDropdownMode = "sheet" | "inline";
@@ -70,15 +71,21 @@ function AutocompleteBase({
const findExactOption = (value: string) => {
const normalizedValue = value.trim().toLowerCase();
return options.find(
- (o) => o.name?.trim().toLowerCase() === normalizedValue,
+ (o) =>
+ o.name?.trim().toLowerCase() === normalizedValue ||
+ o.keywords?.some((k) => k === normalizedValue),
);
};
- const resolveQuerySelection = (nextQuery: string) => {
+ const resolveQuerySelection = (nextQuery: string, matchKeywords = true) => {
const text = nextQuery.trim();
if (!text) return false;
- const exact = findExactOption(text);
+ const exact = matchKeywords
+ ? findExactOption(text)
+ : options.find(
+ (o) => o.name?.trim().toLowerCase() === text.toLowerCase(),
+ );
if (exact) {
setter([exact.id]);
setQuery("");
@@ -99,7 +106,11 @@ function AutocompleteBase({
const filtered = useMemo(() => {
const q = query.trim().toLowerCase();
const base = q
- ? options.filter((o) => o.name?.toLowerCase().includes(q))
+ ? options.filter(
+ (o) =>
+ o.name?.toLowerCase().includes(q) ||
+ o.keywords?.some((k) => k.includes(q)),
+ )
: options;
return preserveOptionOrder
? base
@@ -354,7 +365,12 @@ function AutocompleteBase({
}
const exact = findExactOption(nextQuery);
- if (exact) {
+ // Only auto-fill on an exact NAME match; keyword/acronym matches
+ // (e.g. "dlsu") require an explicit Enter, blur, or click.
+ if (
+ exact &&
+ exact.name.trim().toLowerCase() === nextQuery.trim().toLowerCase()
+ ) {
setter([exact.id]);
setQuery("");
} else {
@@ -371,7 +387,7 @@ function AutocompleteBase({
}
}}
onKeyDown={(e) => {
- if (e.key === "Enter" && resolveQuerySelection(query)) {
+ if (e.key === "Enter" && resolveQuerySelection(query, true)) {
setIsOpen(false);
e.preventDefault();
}
@@ -382,7 +398,7 @@ function AutocompleteBase({
if (Date.now() - lastSelectionRef.current < 250) {
return;
}
- resolveQuerySelection(query);
+ resolveQuerySelection(query, false);
}, 150);
}}
/>
diff --git a/lib/student-forms-access.ts b/lib/student-forms-access.ts
index d6a47dcf..56d5fb65 100644
--- a/lib/student-forms-access.ts
+++ b/lib/student-forms-access.ts
@@ -25,3 +25,49 @@ export const sortUniversityOptions = (universities: University[]) => {
return 0;
});
};
+
+const ACRONYM_STOPWORDS = new Set([
+ "a",
+ "an",
+ "and",
+ "at",
+ "for",
+ "in",
+ "of",
+ "on",
+ "or",
+ "the",
+ "to",
+ "with",
+ "&",
+]);
+
+/**
+ * Generates candidate search acronyms for a university name so that queries
+ * like "dlsu" can match "De La Salle University".
+ *
+ * Returns two variants:
+ * - strict: initials of capitalized words only ("Ateneo de Manila University" -> "amu")
+ * - loose: also includes meaningful lowercase connectors like "de" ("admu")
+ *
+ * @param name The university name.
+ * @returns Unique, lowercased acronym candidates (may be empty).
+ */
+export const universityAcronyms = (name: string): string[] => {
+ const parts = name.split(/[\s-]+/).filter((p) => p.length > 0);
+ const strict = parts
+ .filter((p) => p[0] === p[0].toUpperCase())
+ .map((p) => p[0].toUpperCase())
+ .join("");
+ const loose = parts
+ .filter((p) => {
+ const first = p[0];
+ if (first === first.toUpperCase()) return true;
+ return !ACRONYM_STOPWORDS.has(p.toLowerCase());
+ })
+ .map((p) => p[0].toUpperCase())
+ .join("");
+ return Array.from(
+ new Set([strict, loose].filter(Boolean).map((a) => a.toLowerCase())),
+ );
+};