Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
195051d
feat: added AI Agent MVP (#649)
AkhilTheBoss Dec 22, 2025
f435aba
chore(deps): regen lockfiles
jakeaturner Dec 24, 2025
f67d4e8
fix(SupportAgent): ensure user auth optional
jakeaturner Jan 13, 2026
c0f6f11
fix(deps): override broken dep
jakeaturner Jan 14, 2026
a81fdf1
feat(Search): meilisearch beta testing
jakeaturner Jan 14, 2026
c74baa2
feat(Support): add office hours link
jakeaturner Jan 14, 2026
d713bd2
fix(API): meilisearch URL
jakeaturner Feb 3, 2026
0d86cbd
feat: added account creation banner to support ticket form
AkhilTheBoss Jun 11, 2025
b30e2b2
feat: removed recently edited projects
AkhilTheBoss Jun 12, 2025
4abb683
chore(ui): hide Student ID column from Central Identity Users list
AkhilTheBoss Jun 12, 2025
2649aad
fix(support): added horizontal scrollbar in support dashboard
AkhilTheBoss Jun 16, 2025
c3d45ff
fix(home): add a 3-row layout for XL-screens
AkhilTheBoss Jul 3, 2025
557905e
refactor: removed debugging statements
AkhilTheBoss Jul 3, 2025
d27487b
chore(conductor): remove Users Manager and related unused components …
AkhilTheBoss Jul 5, 2025
4073223
style: improved spacing and layout in campus settings view
AkhilTheBoss Jul 16, 2025
dc5dec7
feat: allow Bulk Download from Commons View
AkhilTheBoss Dec 24, 2025
a58009a
fix: max name length for Lulu
AkhilTheBoss Jan 25, 2026
e60af2a
feat: address comments
AkhilTheBoss Jan 14, 2026
3223be5
feat(store-checkout): added store shipping timeline
AkhilTheBoss Jan 18, 2026
b3b7c94
fix: fix checkout page horizontal overflow on mobile
AkhilTheBoss Jan 19, 2026
ed25a07
fix(Store): shipping timeline layout
jakeaturner Feb 17, 2026
cae3ae5
fix(Campus Settings): cleanup deprecated settings
jakeaturner Feb 17, 2026
0a4ade8
refactor: remove unused component
jakeaturner Feb 17, 2026
0699330
Revert "feat: removed recently edited projects"
jakeaturner Feb 17, 2026
96cb7be
fix(Support): temp disable Ask Benny
jakeaturner Feb 17, 2026
ba3ddda
feat(Search): migrate project search to Meilisearch
jakeaturner Feb 17, 2026
a57244d
feat(Store): email notif to bookstore contact on order fail or rejection
jakeaturner Feb 17, 2026
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
981 changes: 616 additions & 365 deletions client/package-lock.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"react-dom": "^18.3.1",
"react-error-boundary": "^4.0.11",
"react-hook-form": "7.65.0",
"react-markdown": "^10.1.0",
"react-minimal-pie-chart": "^8.2.0",
"react-popper": "^2.3.0",
"react-redux": "^7.2.4",
Expand All @@ -84,6 +85,7 @@
"tus-js-client": "^4.1.0",
"typescript": "^4.9.5",
"usehooks-ts": "^3.1.1",
"vfile": "5.3.7",
"web-vitals": "^0.2.4",
"world-countries": "^5.1.0",
"zod": "^3.22.4"
Expand Down Expand Up @@ -132,7 +134,7 @@
"vite": "^6.4.1",
"vite-tsconfig-paths": "^4.0.5"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "^4.43.0"
"overrides": {
"vfile": "5.3.7"
}
}
2 changes: 1 addition & 1 deletion client/src/Conductor.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@

.conductor-content {
flex: 1 0 auto;
}
}
6 changes: 2 additions & 4 deletions client/src/Conductor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useSelector } from 'react-redux';
import AnonRoute from './components/util/AnonRoute';
import PrivateRoute from './components/util/PrivateRoute';
import Footer from "./components/navigation/Footer";
import ChatBot from "./utils/ChatBot";

import "./Conductor.css";

Expand Down Expand Up @@ -63,8 +64,6 @@ const StoreOrder = lazy(() => import('./screens/conductor/store/order'));
const StoreProduct = lazy(() => import('./screens/conductor/store/product'));
const StoreShipping = lazy(() => import('./screens/conductor/store/shipping'));
const StoreSuccess = lazy(() => import('./screens/conductor/store/success'));
import UserDetails from './components/controlpanel/UserDetails';
const UsersManager = lazy(() => import('./screens/conductor/controlpanel/UsersManager'));
import LoadingSpinner from './components/LoadingSpinner';
const CentralIdentity = lazy(() => import('./screens/conductor/controlpanel/CentralIdentity'));
const CentralIdentityAppLicenses = lazy(() => import('./screens/conductor/controlpanel/CentralIdentity/CentralIdentityAppLicenses'));
Expand Down Expand Up @@ -166,8 +165,6 @@ const Conductor = () => {
<PrivateRoute exact path='/controlpanel/peerreviewrubrics/:mode/:rubricID?' component={PeerReviewRubricManage} />
<PrivateRoute exact path='/controlpanel/store' component={StoreManager} />
<PrivateRoute exact path='/controlpanel/store/orders/:order_id' component={StoreManagerOrderView} />
<PrivateRoute exact path='/controlpanel/usersmanager' component={UsersManager} />
<PrivateRoute exact path='/controlpanel/usersmanager/:uuid' component={UserDetails} />
<PrivateRoute exact path='/events/:eventID/:status?' component={EventRegistration} unAuthSrc="eventregistration" />
<Route exact path="/download/:projectID/:fileID" component={PermanentLinkDownload} />
<Route exact path='/peerreview/:id' component={PeerReviewPage} />
Expand Down Expand Up @@ -197,6 +194,7 @@ const Conductor = () => {
<Route component={PageNotFound} />
</Switch>
</Suspense>
{/* <ChatBot /> */}
</div>
<Footer />
</CartProvider>
Expand Down
4 changes: 4 additions & 0 deletions client/src/Platform.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ const Platform = () => {
script.src =
"https://cdn.libretexts.net/libretexts-support-widget.min.js";
body.appendChild(script);

return () => {
body.removeChild(script);
};
}
}, []);

Expand Down
64 changes: 61 additions & 3 deletions client/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,17 @@ class API {
return res;
}

async booksSearchV2(params: BookSearchParams) {
const res = await axios.get<
ConductorSearchResponse<"books"> & ConductorBaseResponse
>("/search/books-v2", {
params: {
...params,
},
});
return res;
}

async homeworkSearch(params: HomeworkSearchParams) {
const res = await axios.get<
ConductorSearchResponse<"homework"> & ConductorBaseResponse
Expand Down Expand Up @@ -1285,6 +1296,17 @@ class API {
return res;
}

async projectsSearchV2(params: ProjectSearchParams) {
const res = await axios.get<
ConductorSearchResponse<"projects"> & ConductorBaseResponse
>("/search/projects-v2", {
params: {
...params,
},
});
return res;
}

async usersSearch(params: UserSearchParams) {
const res = await axios.get<
ConductorSearchResponse<"users"> & ConductorBaseResponse
Expand Down Expand Up @@ -1527,16 +1549,14 @@ class API {

async bulkDownloadFiles(
projectID: string,
fileIDs: string[],
emailToNotify: string
fileIDs: string[]
) {
const arrQuery = fileIDs.map((id) => `fileID=${id}`).join(`&`);
const res = await axios.get<{ file?: string } & ConductorBaseResponse>(
`/project/${projectID}/files/bulk`,
{
params: {
fileIDs: arrQuery,
emailToNotify,
},
}
);
Expand Down Expand Up @@ -2119,6 +2139,44 @@ class API {
>(`/store/checkout/session/${order_id}`);
return res.data;
}

/**
* Create a new agent session for LangGraph
*/
async createAgentSession() {
const res = await axios.post<
{
sessionId: string;
} & ConductorBaseResponse
>("/agent/create-session");

return res.data;
}

async queryLangGraphAgent(query: string, sessionId: string) {
const res = await axios.post<
{
response: string;
sources: Array<{
number: number;
title: string;
description: string;
slug?: string;
url: string;
relevanceScore?: number;
source: 'kb' | 'web';
}>;
query: string;
timestamp: string;
} & ConductorBaseResponse
>("/agent/query-langgraph", {
query,
sessionId,
});

return res.data;
}

}

export default new API();
56 changes: 32 additions & 24 deletions client/src/components/FilesManager/FilesManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ interface FilesManagerProps extends SegmentProps {
projectID: string;
toggleFilesManager: () => void;
canViewDetails: boolean;
allowBulkDownload?: boolean;
projectHasDefaultLicense?: boolean;
projectVisibility?: string;
}
Expand All @@ -66,6 +67,7 @@ const FilesManager: React.FC<FilesManagerProps> = ({
projectID,
toggleFilesManager,
canViewDetails = false,
allowBulkDownload = false,
projectHasDefaultLicense = false,
projectVisibility = "private",
}) => {
Expand Down Expand Up @@ -438,7 +440,7 @@ const FilesManager: React.FC<FilesManagerProps> = ({
async function handleBulkDownload(ids: string[]) {
try {
setDownloadLoading(true);
const res = await api.bulkDownloadFiles(projectID, ids, user.email);
const res = await api.bulkDownloadFiles(projectID, ids);

if (!res.data || res.data.err) {
throw new Error("Unable to download files. Please try again later.");
Expand Down Expand Up @@ -705,12 +707,14 @@ const FilesManager: React.FC<FilesManagerProps> = ({
</Message>
)}
<Segment.Group size="large" raised className="mb-4">
{canViewDetails && (
{(canViewDetails || allowBulkDownload) && (
<Segment>
<p style={{ fontSize: "0.9em" }} className="mb-4">
If your project has supporting files, use this tool to upload and
organize them.
</p>
{canViewDetails && (
<p style={{ fontSize: "0.9em" }} className="mb-4">
If your project has supporting files, use this tool to upload and
organize them.
</p>
)}
<Button.Group
fluid
widths="7"
Expand All @@ -722,18 +726,22 @@ const FilesManager: React.FC<FilesManagerProps> = ({
: ""
}
>
<Button color="green" onClick={() => setShowUploader(true)}>
<Icon name="upload" />
Upload
</Button>
<Button
color="green"
className="!bg-green-600"
onClick={() => setShowAddFolder(true)}
>
<Icon name="add" />
New Folder
</Button>
{canViewDetails && (
<>
<Button color="green" onClick={() => setShowUploader(true)}>
<Icon name="upload" />
Upload
</Button>
<Button
color="green"
className="!bg-green-600"
onClick={() => setShowAddFolder(true)}
>
<Icon name="add" />
New Folder
</Button>
</>
)}
{itemsChecked > 1 && (
<Button
color="blue"
Expand All @@ -754,7 +762,7 @@ const FilesManager: React.FC<FilesManagerProps> = ({
)}
</Button>
)}
{itemsChecked > 1 && (
{canViewDetails && itemsChecked > 1 && (
<Button
color="teal"
disabled={itemsChecked < 1}
Expand All @@ -766,7 +774,7 @@ const FilesManager: React.FC<FilesManagerProps> = ({
Move
</Button>
)}
{itemsChecked > 0 && (
{canViewDetails && itemsChecked > 0 && (
<Button
color="yellow"
disabled={itemsChecked < 1}
Expand All @@ -780,7 +788,7 @@ const FilesManager: React.FC<FilesManagerProps> = ({
Change Access
</Button>
)}
{canBulkTag && (
{canViewDetails && canBulkTag && (
<Button
color="purple"
disabled={itemsChecked < 1}
Expand All @@ -792,7 +800,7 @@ const FilesManager: React.FC<FilesManagerProps> = ({
Bulk Tag
</Button>
)}
{itemsChecked > 1 && (
{canViewDetails && itemsChecked > 1 && (
<Button
color="red"
disabled={itemsChecked < 1}
Expand All @@ -819,7 +827,7 @@ const FilesManager: React.FC<FilesManagerProps> = ({
<Table basic attached="bottom">
<Table.Header>
<Table.Row>
{canViewDetails && (
{(canViewDetails || allowBulkDownload) && (
<Table.HeaderCell key="check" collapsing={true}>
<input
type="checkbox"
Expand Down Expand Up @@ -864,7 +872,7 @@ const FilesManager: React.FC<FilesManagerProps> = ({
if (!isTailwindLg) return MobileTableRow(item);
return (
<Table.Row className="h-[60px]" key={item.fileID}>
{canViewDetails && (
{(canViewDetails || allowBulkDownload) && (
<Table.Cell>
<input
type="checkbox"
Expand Down
7 changes: 4 additions & 3 deletions client/src/components/Home/PinnedProjects/PinnedProjects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface PinnedProjectsInterface {}
const PinnedProjects: React.FC<PinnedProjectsInterface> = () => {
const { openModal, closeAllModals } = useModals();
const isTailwindLg = useMediaQuery({ minWidth: 1024 });
const isXL = useMediaQuery({ minWidth: 1280 });
const { data, isLoading } = usePinnedProjects();

const onShowPinnedModal = () => {
Expand Down Expand Up @@ -64,7 +65,7 @@ const PinnedProjects: React.FC<PinnedProjectsInterface> = () => {
return <NoPinnedMessage />;
}
return (
<Card.Group itemsPerRow={2} className={classList}>
<Card.Group itemsPerRow={isXL ? 1 : 2} className={classList}>
{i.projects?.map((item) =>
typeof item === "string" ? null : (
<ProjectCard project={item} key={item.projectID} />
Expand All @@ -86,7 +87,7 @@ const PinnedProjects: React.FC<PinnedProjectsInterface> = () => {
return <NoPinnedMessage />;
}
return (
<Card.Group itemsPerRow={2} className={classList}>
<Card.Group itemsPerRow={isXL ? 1 : 2} className={classList}>
{data?.map((i) => {
if (typeof i.projects === "string") return null;
return i.projects?.map((item) =>
Expand All @@ -101,7 +102,7 @@ const PinnedProjects: React.FC<PinnedProjectsInterface> = () => {
});

return items;
}, [data]);
}, [data, isXL]);

return (
<Segment padded={Object.entries(data || {}).length > 0} loading={isLoading} className="!pb-10">
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/commons/CommonsCatalog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ const CommonsCatalog = () => {
) {
try {
setBooksLoading(true);
const res = await api.booksSearch({
const res = await api.booksSearchV2({
...(query && { searchQuery: query }),
page: page,
limit: ITEMS_PER_PAGE,
Expand Down Expand Up @@ -611,7 +611,7 @@ const CommonsCatalog = () => {
) {
try {
setProjectsLoading(true);
const res = await api.projectsSearch({
const res = await api.projectsSearchV2({
searchQuery: query,
page,
limit: ITEMS_PER_PAGE,
Expand Down
12 changes: 11 additions & 1 deletion client/src/components/controlpanel/CampusSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "./ControlPanel.css";
import { useRef, useState } from "react";
import { useRef, useState, useEffect } from "react";
import { Link } from "react-router-dom";
import {
Breadcrumb,
Expand All @@ -15,6 +15,16 @@ import { useTypedSelector } from "../../state/hooks";
const CampusSettings = () => {
//Global state
const org = useTypedSelector((state) => state.org);
const user = useTypedSelector((state) => state.user);

useEffect(() => {
if (!user || !user.uuid) { // Ensure user is loaded before checking roles
return;
}
if (!user.isCampusAdmin && !user.isSuperAdmin && !user.isSupport) {
window.location.href = "/home";
}
}, [user]);

const settingsFormRef =
useRef<React.ElementRef<typeof CampusSettingsForm>>(null);
Expand Down
Loading