diff --git a/src/api/upload.js b/src/api/upload.js index b6c235f9f..d62707026 100644 --- a/src/api/upload.js +++ b/src/api/upload.js @@ -96,6 +96,22 @@ export const getUploadByIdApi = (uploadId, retries) => { }); }; +// Updating an Upload's name and description +export const updateUploadApi = (uploadId, uploadName, uploadDescription) => { + const url = endpoints.upload.update(uploadId); + return sendRequest({ + url, + method: "PATCH", + headers: { + Authorization: getToken(), + }, + body: { + uploadName, + uploadDescription, + }, + }); +}; + // Getting a Upload Summary export const getUploadSummaryApi = (uploadId) => { const url = endpoints.upload.getSummary(uploadId); diff --git a/src/app/HomeClient/HomeClient.jsx b/src/app/HomeClient/HomeClient.jsx index 8e0490e7e..b94368b3d 100644 --- a/src/app/HomeClient/HomeClient.jsx +++ b/src/app/HomeClient/HomeClient.jsx @@ -1,218 +1,187 @@ /* - SPDX-FileCopyrightText: 2025 Tiyasa Kundu (tiyasakundu20@gmail.com) + SPDX-FileCopyrightText: Š 2025 FOSSology Contributors + SPDX-License-Identifier: GPL-2.0-only +*/ -SPDX-License-Identifier: GPL-2.0-only +"use client"; - 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. +import { useState } from "react"; - 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. -*/ +const HomeClient = () => { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const [isLoading, setIsLoading] = useState(false); -'use client'; - -import { useState, useEffect } from 'react'; -import { useRouter, useSearchParams } from 'next/navigation'; -import { cn } from "@/lib/utils" - -import { Eye, EyeOff } from "lucide-react" -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { - Alert, - AlertTitle, - AlertDescription, -} from '@/components/ui/alert'; -import { - Card, - CardHeader, - CardTitle, - CardDescription, - CardContent, -} from "@/components/ui/card" - -import fetchToken from '@/services/auth'; -import { getUserSelf } from '@/services/users'; -import { fetchAllGroups } from '@/services/groups'; -import routes from '@/constants/routes'; -import { isAuth } from '@/shared/authHelper'; - -export default function HomeClient() { - const router = useRouter(); - const searchParams = useSearchParams(); - - const [values, setValues] = useState({ username: '', password: '' }); - const [loading, setLoading] = useState(false); - const [showError, setShowError] = useState(false); - const [errorMessage, setErrorMessage] = useState(null); - - const { username, password } = values; - - const handleChange = (name) => (event) => { - setValues({ ...values, [name]: event.target.value }); + const handleLogin = async () => { + setIsLoading(true); + setTimeout(() => setIsLoading(false), 2000); }; - const handleSubmit = async (event) => { - event.preventDefault(); - setLoading(true); - try { - await fetchToken(values); - await getUserSelf(); - await fetchAllGroups(); - router.push(routes.browse); - } catch (err) { - setLoading(false); - setErrorMessage(err.message); - setShowError(true); - } - }; - - useEffect(() => { - const message = searchParams.get('message'); - if (message) { - setErrorMessage(message); - setShowError(true); - router.replace(window.location.pathname); - } - }, [searchParams, router]); - - const [showPassword, setShowPassword] = useState(false) + const features = [ + { icon: "📁", title: "Upload Files", desc: "Upload files into the FOSSology repository" }, + { icon: "đŸ“Ļ", title: "Unpack Files", desc: "Unpack zip, tar, bz2, iso and many others" }, + { icon: "đŸŒŗ", title: "Browse Trees", desc: "Browse upload file trees easily" }, + { icon: "🔍", title: "Scan Licenses", desc: "Scan for software licenses automatically" }, + { icon: "ÂŠī¸", title: "Scan Copyrights", desc: "Scan for copyrights and author information" }, + { icon: "📊", title: "Compare Trees", desc: "View side-by-side license and bucket differences" }, + ]; return ( -
-
- {/* Left: Intro Content */} -
-

- Getting Started with FOSSology -

- -

- FOSSology is a framework for software analysis tools. With it, you can: -

- -
    -
  • Upload files into the fossology repository.
  • -
  • Unpack files (zip, tar, bz2, iso's, and many others) into its component files.
  • -
  • Browse upload file trees.
  • -
  • View file contents and meta data.
  • -
  • Scan for software licenses.
  • -
  • Scan for copyrights and other author information.
  • -
  • View side-by-side license and bucket differences between file trees.
  • -
  • Tag and attach notes to files.
  • -
  • Report files based on your own custom classification scheme.
  • -
- -

Where to Begin

-
    -
  • The menu at the top contains all the primary capabilities of FOSSology.
  • -
  • - Login: Depending on your account's access rights, you may be able to upload files, - schedule analysis tasks, or even add new users. -
  • -
-
- - {/* Right: Login Form */} - {!isAuth() && ( - - - - Log in to your account - - - Hello there! Welcome to FOSSology - -

- This login uses HTTP, so passwords are transmitted in plain text. This is not a secure connection. +

+ + {/* ── Hero Section ── */} +
+ + {/* Badge */} + + Open Source Compliance Tool + + + {/* Title */} +

+ Welcome to{" "} + + FOSSology + +

+ + {/* Subtitle */} +

+ A framework for software analysis, license compliance, + and copyright detection at scale. +

+ + {/* ── Main Card ── */} +
+ + {/* Features Grid */} +
+ {features.map((f, i) => ( +
+
{f.icon}
+

+ {f.title} +

+

+ {f.desc}

- - - - {showError && ( -
- - Error -
- - An error occurred - - - {errorMessage} - -
-
- )} -
-
- - + + {/* Login Card */} +
+ +

+ Sign In +

+

+ Access your FOSSology account +

+ + {/* Warning */} +
+

+ âš ī¸ HTTP connection — passwords sent as plain text +

+
+ + {/* Username */} +
+ + setUsername(e.target.value)} + placeholder="Enter username" + className="w-full bg-white/10 border border-white/20 + rounded-lg px-4 py-3 text-white + placeholder-slate-500 text-sm + focus:outline-none focus:border-blue-400 + focus:bg-white/15 transition-all duration-200" + /> +
+ + {/* Password */} +
+ +
+ setPassword(e.target.value)} + placeholder="Enter password" + className="w-full bg-white/10 border border-white/20 + rounded-lg px-4 py-3 text-white + placeholder-slate-500 text-sm pr-12 + focus:outline-none focus:border-blue-400 + focus:bg-white/15 transition-all duration-200" /> +
+
+ + {/* Login Button */} + + +
+
+
-
- -
- - - - -
-
- - - - - - )} + {/* ── Footer ── */} +
+

+ FOSSology — Open Source License Compliance +

+
); -} +}; + +export default HomeClient; \ No newline at end of file diff --git a/src/app/admin/license/create/AddLicenseClient.jsx b/src/app/admin/license/create/AddLicenseClient.jsx index f4ba5b633..b3cf203af 100644 --- a/src/app/admin/license/create/AddLicenseClient.jsx +++ b/src/app/admin/license/create/AddLicenseClient.jsx @@ -22,7 +22,16 @@ SPDX-License-Identifier: GPL-2.0-only import React, { useState } from "react"; // Widgets -import { InputContainer, Button } from "@/components/Widgets"; +import { Alert, InputContainer, Button } from "@/components/Widgets"; + +// Services +import { createCandidateLicense } from "@/services/licenses"; + +// Helpers +import { handleError } from "@/shared/helper"; + +// Constants +import messages from "@/constants/messages"; const AddLicensePage = () => { const options = [ @@ -47,6 +56,10 @@ const AddLicensePage = () => { obligations: "", }); + const [showMessage, setShowMessage] = useState(false); + const [message, setMessage] = useState({ type: "success", text: "" }); + const [loading, setLoading] = useState(false); + const handleChange = (e) => { const { name, value } = e.target; setFormData((prev) => ({ @@ -57,8 +70,31 @@ const AddLicensePage = () => { const handleSubmit = (e) => { e.preventDefault(); - // Submit formData via API here - console.log("Submitted:", formData); + setLoading(true); + createCandidateLicense({ + shortName: formData.shortName, + fullName: formData.fullName, + text: formData.licenseText, + risk: formData.riskLevel ? Number(formData.riskLevel) : undefined, + licenseUrl: formData.url, + mergeRequest: false, + }) + .then(() => { + setMessage({ type: "success", text: messages.createdLicense }); + setFormData({ + active: "", checked: "", spdxCompatible: "", shortName: "", + fullName: "", licenseText: "", textUpdatable: "", detectorType: "", + url: "", publicNotes: "", conclusion: "", reportedLicense: "", + riskLevel: "", obligations: "", + }); + }) + .catch((error) => { + handleError(error, setMessage); + }) + .finally(() => { + setShowMessage(true); + setLoading(false); + }); }; return ( @@ -67,6 +103,13 @@ const AddLicensePage = () => {

Add License


+ {showMessage && ( + + )}
{ Associated Obligations -
diff --git a/src/app/organize/uploads/delete/DeleteUploadsClient.jsx b/src/app/organize/uploads/delete/DeleteUploadsClient.jsx index 31253909c..e0525b94a 100644 --- a/src/app/organize/uploads/delete/DeleteUploadsClient.jsx +++ b/src/app/organize/uploads/delete/DeleteUploadsClient.jsx @@ -30,6 +30,7 @@ import { getUploadsFolderId, deleteUploadsbyId, } from "@/services/organizeUploads"; +import { handleError } from "@/shared/helper"; const UploadDeletePage = () => { const initialState = { @@ -104,13 +105,19 @@ const UploadDeletePage = () => { useEffect(() => { getAllFolders() .then(setFolderList) - .catch((err) => console.error("Error fetching folders", err)); + .catch((error) => { + handleError(error, setMessage); + setShowMessage(true); + }); }, []); useEffect(() => { getUploadsFolderId(deleteUploadFolderData.folderId) .then(setUploadFolderList) - .catch((err) => console.error("Error fetching uploads", err)); + .catch((error) => { + handleError(error, setMessage); + setShowMessage(true); + }); }, [deleteUploadFolderData.folderId]); return ( diff --git a/src/app/organize/uploads/edit/EditUploadsClient.jsx b/src/app/organize/uploads/edit/EditUploadsClient.jsx index afc26ea5e..d7b809ade 100644 --- a/src/app/organize/uploads/edit/EditUploadsClient.jsx +++ b/src/app/organize/uploads/edit/EditUploadsClient.jsx @@ -20,9 +20,10 @@ import React, { useState, useEffect } from "react"; import { Alert, Button, InputContainer } from "@/components/Widgets"; +import messages from "@/constants/messages"; import { getAllFolders } from "@/services/folders"; import { getUploadsFolderId } from "@/services/organizeUploads"; -import { getUploadById } from "@/services/upload"; +import { getUploadById, updateUpload } from "@/services/upload"; import { handleError } from "@/shared/helper"; const UploadEditPage = () => { @@ -53,14 +54,21 @@ const UploadEditPage = () => { const handleSubmit = (e) => { e.preventDefault(); - - // Submit API logic here (e.g., updateUploadInfo) - // For now, simulate success message: - setMessage({ - type: "success", - text: "Upload properties updated (not implemented)", - }); - setShowMessage(true); + if (!editUploadFolderData.uploadId) return; + + updateUpload( + editUploadFolderData.uploadId, + editUploadFolderData.uploadName, + editUploadFolderData.uploadDescription + ) + .then(() => { + setMessage({ type: "success", text: messages.updatedUploadProps }); + setShowMessage(true); + }) + .catch((error) => { + handleError(error, setMessage); + setShowMessage(true); + }); }; useEffect(() => { diff --git a/src/app/upload/oneShotAnalysis/OneShotAnalysisClient.jsx b/src/app/upload/oneShotAnalysis/OneShotAnalysisClient.jsx index 0b49aa7f7..47103be03 100644 --- a/src/app/upload/oneShotAnalysis/OneShotAnalysisClient.jsx +++ b/src/app/upload/oneShotAnalysis/OneShotAnalysisClient.jsx @@ -24,15 +24,16 @@ import React from "react"; import { Button, InputContainer } from "@/components/Widgets"; const OneShotAnalysisPage = () => { + const [selectedFile, setSelectedFile] = React.useState(null); + const handleSubmit = (e) => { e.preventDefault(); - // TODO: Add file handling and real-time license analysis logic here - console.log("Analyze button clicked"); + if (!selectedFile) return; + // File is stored in selectedFile, ready for API integration }; const handleChange = (e) => { - // TODO: Handle file input change here - console.log("Selected file:", e.target.files[0]); + setSelectedFile(e.target.files[0] || null); }; return ( diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 89763742c..1e7480b60 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -60,6 +60,7 @@ const endpoints = { upload: { uploadCreate: () => `${apiUrl}/uploads`, getId: (uploadId) => `${apiUrl}/uploads/${uploadId}`, + update: (uploadId) => `${apiUrl}/uploads/${uploadId}`, getSummary: (uploadId) => `${apiUrl}/uploads/${uploadId}/summary`, getLicense: (uploadId) => `${apiUrl}/uploads/${uploadId}/licenses`, }, diff --git a/src/constants/messages.js b/src/constants/messages.js index 829d5c380..e4c545f9b 100644 --- a/src/constants/messages.js +++ b/src/constants/messages.js @@ -33,6 +33,7 @@ const messages = { createdFolder: "Successfully created the folder", deletedFolder: "Successfully deleted the folder", updatedFolderProps: "Successfully updated the folder properties", + updatedUploadProps: "Successfully updated the upload properties", movedFolder: "Successfully moved the folder", copiedFolder: "Successfully copied the folder", unlinkedFolder: "Successfully unlinked the folder", diff --git a/src/services/upload.js b/src/services/upload.js index 375b51eb1..eaf920a45 100644 --- a/src/services/upload.js +++ b/src/services/upload.js @@ -18,6 +18,7 @@ import { createUploadApi, getUploadByIdApi, + updateUploadApi, createUploadVcsApi, createUploadUrlApi, getUploadSummaryApi, @@ -64,6 +65,13 @@ export const getUploadById = (uploadId, retries) => { }); }; +// Updating an Upload's name and description +export const updateUpload = (uploadId, uploadName, uploadDescription) => { + return updateUploadApi(uploadId, uploadName, uploadDescription).then((res) => { + return res; + }); +}; + // Getting a Upload Summary export const getUploadSummary = (uploadId) => { return getUploadSummaryApi(uploadId).then((res) => {