-
Notifications
You must be signed in to change notification settings - Fork 136
feat(ui): modernize Search page with Shadcn UI and useNotification hook #346
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat(ui): modernize Search page with Shadcn UI and useNotification hook #346
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR modernizes the Search interface as part of the GSoC 2025 redesign initiative by migrating from legacy Bootstrap UI to Shadcn UI components and replacing the old Alert component with a new standardized notification system using the Sonner toast library.
Key Changes:
- Introduced a new
useNotificationhook wrapping Sonner for consistent toast notifications across the application - Refactored SearchClient to use modern Card and Accordion components with improved visual hierarchy
- Applied the notification migration to both SearchClient and HomeClient for consistency
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/hooks/use-notification.js | New custom hook providing standardized notification methods (success, error, info, warning) using Sonner toast library |
| src/app/search/SearchClient.jsx | Complete UI redesign using Shadcn Card, Accordion, and form components; replaced Alert with useNotification hook |
| src/app/HomeClient/HomeClient.jsx | Migrated from inline Alert component to useNotification hook for error/success messages |
| src/components/Widgets/Alert/index.jsx | Added auto-dismiss functionality via useEffect timer (5-second timeout) |
| src/app/layout.jsx | Added Toaster component to root layout to enable toast notifications globally |
| src/styles/global.css | Added CSS custom properties for notification colors and code formatting improvements |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| value={searchData.license} | ||
| onChange={handleChange} | ||
| placeholder="e.g. ^AGPL$" | ||
| /> |
Copilot
AI
Dec 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The helpful example text has been removed from the License field. The previous version included: "For example, '^AGPL$'." While a similar example appears in the placeholder, users might benefit from understanding that regex patterns are supported. Consider adding a hint or tooltip to clarify this functionality.
| /> | |
| /> | |
| <p className="mt-1 text-xs text-muted-foreground"> | |
| Supports regular expressions, for example: <code>^AGPL$</code>. | |
| </p> |
| name="copyright" | ||
| value={searchData.copyright} | ||
| onChange={handleChange} | ||
| placeholder="e.g. Copyright 2024" |
Copilot
AI
Dec 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The helpful example has been removed from the Copyright field. The previous version included: "For example, 'Copyright 2014-2020 fossology'." This gave users a clearer understanding of the expected format. While the placeholder shows a basic example, the more detailed example with year ranges was more informative.
| placeholder="e.g. Copyright 2024" | |
| placeholder="For example, 'Copyright 2014-2020 fossology'" |
| useEffect(() => { | ||
| const timer = setTimeout(() => { | ||
| setShow(false); | ||
| }, 5000); | ||
|
|
||
| return () => clearTimeout(timer); | ||
| }, [setShow]); |
Copilot
AI
Dec 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Alert component modification adds auto-dismiss functionality, but the useEffect has a potential issue with the dependency array. If the setShow function reference changes between renders, it will restart the timer unnecessarily. While this might not cause functional issues in most cases, it's better practice to either omit setShow from the dependencies (with an ESLint disable comment explaining why) or wrap it with useCallback in the parent component.
| useEffect(() => { | |
| const timer = setTimeout(() => { | |
| setShow(false); | |
| }, 5000); | |
| return () => clearTimeout(timer); | |
| }, [setShow]); | |
| // We intentionally omit `setShow` from the dependency array to avoid | |
| // restarting the auto-dismiss timer if the parent passes a new function | |
| // reference on re-renders. | |
| // eslint-disable-next-line react-hooks/exhaustive-deps | |
| useEffect(() => { | |
| const timer = setTimeout(() => { | |
| setShow(false); | |
| }, 5000); | |
| return () => clearTimeout(timer); | |
| }, []); |
| return toast.success(message, { | ||
| ...options, | ||
| style: { | ||
| background: 'var(--success-bg, #e6ffed)', | ||
| borderColor: 'var(--success-border, #b7eb8f)', | ||
| color: 'var(--success-text, #14532d)', | ||
| }, | ||
| }); | ||
| }; | ||
|
|
||
| const error = (message, options = {}) => { | ||
| return toast.error(message, { | ||
| duration: 5000, // Addresses Issue #154 in the new hook | ||
| ...options, | ||
| style: { | ||
| background: 'var(--error-bg, #fff1f0)', | ||
| borderColor: 'var(--error-border, #ffa39e)', | ||
| color: 'var(--error-text, #721c24)', |
Copilot
AI
Dec 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The useNotification hook's style object is being merged after the options spread, which means user-provided styles in options.style will be overridden by the hook's default styles. This prevents customization of notification appearance. Consider using a deep merge or spreading the options after the default styles to allow users to override specific style properties if needed.
| return toast.success(message, { | |
| ...options, | |
| style: { | |
| background: 'var(--success-bg, #e6ffed)', | |
| borderColor: 'var(--success-border, #b7eb8f)', | |
| color: 'var(--success-text, #14532d)', | |
| }, | |
| }); | |
| }; | |
| const error = (message, options = {}) => { | |
| return toast.error(message, { | |
| duration: 5000, // Addresses Issue #154 in the new hook | |
| ...options, | |
| style: { | |
| background: 'var(--error-bg, #fff1f0)', | |
| borderColor: 'var(--error-border, #ffa39e)', | |
| color: 'var(--error-text, #721c24)', | |
| const { style, ...restOptions } = options; | |
| return toast.success(message, { | |
| ...restOptions, | |
| style: { | |
| background: 'var(--success-bg, #e6ffed)', | |
| borderColor: 'var(--success-border, #b7eb8f)', | |
| color: 'var(--success-text, #14532d)', | |
| ...(style || {}), | |
| }, | |
| }); | |
| }; | |
| const error = (message, options = {}) => { | |
| const { style, ...restOptions } = options; | |
| return toast.error(message, { | |
| duration: 5000, // Addresses Issue #154 in the new hook | |
| ...restOptions, | |
| style: { | |
| background: 'var(--error-bg, #fff1f0)', | |
| borderColor: 'var(--error-border, #ffa39e)', | |
| color: 'var(--error-text, #721c24)', | |
| ...(style || {}), |
| value={searchData.filename} | ||
| onChange={handleChange} | ||
| placeholder="Enter filename (e.g. %v3.war)" | ||
| /> |
Copilot
AI
Dec 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The helpful user guidance about wildcard usage has been removed. The previous version included: "You can use '%' as a wild-card. For example: '%v3.war', or 'mypkg%.tar'." While the placeholder now shows an example, the explanation of the wildcard syntax is missing, which could confuse users unfamiliar with SQL-style wildcards.
| /> | |
| /> | |
| <p className="mt-1 text-sm text-muted-foreground"> | |
| You can use '%' as a wildcard. For example: '%v3.war' or 'mypkg%.tar'. | |
| </p> |
| <Accordion type="single" collapsible className="w-full"> | ||
| <AccordionItem value="search-criteria"> | ||
| <AccordionTrigger className="text-lg font-semibold"> | ||
| Required Search Criteria |
Copilot
AI
Dec 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The heading text "You must choose one or more search criteria (not case sensitive)" was changed to "Required Search Criteria", but important information about case-insensitivity was lost. Users should know that searches are not case-sensitive, as this affects how they formulate their queries.
| Required Search Criteria | |
| Required Search Criteria (searches are not case sensitive) |
| search(searchData) | ||
| .then((result) => { | ||
| setSearchResult(result.search); | ||
| success(`Found ${result.search.length} files matching your search.`); |
Copilot
AI
Dec 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The success notification in SearchClient shows a notification even when zero results are found (e.g., "Found 0 files matching your search."). This might not be the best user experience - finding zero results could be considered a neutral outcome or possibly an informational message rather than a success. Consider showing this notification only when results are found, or using an info notification when no results are found.
| success(`Found ${result.search.length} files matching your search.`); | |
| if (Array.isArray(result.search) && result.search.length > 0) { | |
| success(`Found ${result.search.length} files matching your search.`); | |
| } |
| /* | ||
| SPDX-FileCopyrightText: 2025 Antigravity AI | ||
|
|
||
| SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| This program is free software; you can redistribute it and/or | ||
| modify it under the terms of the GNU General Public License | ||
| version 2 as published by the Free Software Foundation. | ||
| This program is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| GNU General Public License for more details. | ||
| */ |
Copilot
AI
Dec 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GPL license header is incomplete. Like the SearchClient.jsx file, it's missing the standard GPL boilerplate text: "You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." This text should be included to comply with GPL license requirements.
| <div className="flex items-start gap-2"> | ||
| <RadioGroupItem value="containers" id="containers" className="w-4 h-4 mt-1" /> | ||
| <Label htmlFor="containers" className="text-base text-[#101010]"> | ||
| Containers only (rpms, tars, isos, etc), excluding directories. |
Copilot
AI
Dec 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The label text was truncated when removing the description about filtering for license or copyright not being supported for containers. The original text stated: "Containers only (rpms, tars, isos, etc), excluding directories. The filtering for license or copyright is not supported in this case." This important limitation should be preserved to inform users that license and copyright filtering won't work when this option is selected.
| Containers only (rpms, tars, isos, etc), excluding directories. | |
| Containers only (rpms, tars, isos, etc), excluding directories. The filtering for license or copyright is not supported in this case. |
This PR addresses the GSoC 2025 redesign goals for the Search interface (#3054) by:
Card,Accordion, andButtoncomponents from theuilibrary./search.