-
Notifications
You must be signed in to change notification settings - Fork 514
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
Update license Component for Github SBOM #9755
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's keep the script in typescript similar to the other postinstall script |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,49 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import fs from "fs"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import fetch from "node-fetch"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const fetchSBOMData = async (url) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const response = await fetch(url, { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
headers: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Accept: "application/vnd.github+json", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"X-GitHub-Api-Version": "2022-11-28", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!response.ok) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
throw new Error(`Error fetching SBOM data from ${url}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return await response.json(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix ESM compatibility and add GitHub authentication. The code has several issues that need to be addressed:
Apply these changes: +// Add to package.json
+{
+ "type": "module"
+}
import fs from "fs";
import fetch from "node-fetch";
+import dotenv from "dotenv";
+
+dotenv.config();
+
+const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
const fetchSBOMData = async (url) => {
const response = await fetch(url, {
headers: {
Accept: "application/vnd.github+json",
+ Authorization: `Bearer ${GITHUB_TOKEN}`,
"X-GitHub-Api-Version": "2022-11-28",
},
});
🧰 Tools🪛 GitHub Actions: Lint Code Base[error] 1-1: Cannot use import statement outside a module. Need to set "type": "module" in package.json or use .mjs extension |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const fetchData = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const feUrl = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"https://api.github.com/repos/ohcnetwork/care_fe/dependency-graph/sbom"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const beUrl = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"https://api.github.com/repos/ohcnetwork/care/dependency-graph/sbom"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [frontendData, backendData] = await Promise.all([ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fetchSBOMData(feUrl), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fetchSBOMData(beUrl), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Write frontend SBOM data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fs.writeFileSync( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"./src/components/Licenses/feBomData.json", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
JSON.stringify(frontendData, null, 2), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Write backend SBOM data | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fs.writeFileSync( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"./src/components/Licenses/beBomData.json", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
JSON.stringify(backendData, null, 2), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.log( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"SBOM data successfully saved as feBomData.json and beBomData.json", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.error("Error fetching SBOM data:", error.message); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+17
to
+47
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve error handling and configuration management. The function needs several improvements:
Apply these changes: +const CONFIG = {
+ repositories: {
+ frontend: "ohcnetwork/care_fe",
+ backend: "ohcnetwork/care"
+ },
+ outputPath: "./src/components/Licenses"
+};
+
+const validateSBOMData = (data) => {
+ if (!data?.sbom?.packages) {
+ throw new Error("Invalid SBOM data structure");
+ }
+};
+
const fetchData = async () => {
- const feUrl =
- "https://api.github.com/repos/ohcnetwork/care_fe/dependency-graph/sbom";
- const beUrl =
- "https://api.github.com/repos/ohcnetwork/care/dependency-graph/sbom";
+ const feUrl = `https://api.github.com/repos/${CONFIG.repositories.frontend}/dependency-graph/sbom`;
+ const beUrl = `https://api.github.com/repos/${CONFIG.repositories.backend}/dependency-graph/sbom`;
try {
const [frontendData, backendData] = await Promise.all([
fetchSBOMData(feUrl),
fetchSBOMData(beUrl),
]);
+ validateSBOMData(frontendData);
+ validateSBOMData(backendData);
+
+ fs.mkdirSync(CONFIG.outputPath, { recursive: true });
+
// Write frontend SBOM data
fs.writeFileSync(
- "./src/components/Licenses/feBomData.json",
+ `${CONFIG.outputPath}/feBomData.json`,
JSON.stringify(frontendData, null, 2),
);
// Write backend SBOM data
fs.writeFileSync(
- "./src/components/Licenses/beBomData.json",
+ `${CONFIG.outputPath}/beBomData.json`,
JSON.stringify(backendData, null, 2),
);
console.log(
"SBOM data successfully saved as feBomData.json and beBomData.json",
);
} catch (error) {
console.error("Error fetching SBOM data:", error.message);
+ process.exit(1);
}
}; 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fetchData(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,35 @@ | ||
import dayjs from "dayjs"; | ||
import React, { useState } from "react"; | ||
import { CopyToClipboard } from "react-copy-to-clipboard"; | ||
import { useTranslation } from "react-i18next"; | ||
|
||
import Card from "@/CAREUI/display/Card"; | ||
import CareIcon from "@/CAREUI/icons/CareIcon"; | ||
|
||
import beBomData from "@/components/Licenses/be-sbom.json"; | ||
import feBomData from "@/components/Licenses/fe-sbom.json"; | ||
import licenseUrls from "@/components/Licenses/licenseUrls.json"; | ||
|
||
import beBomData from "./beBomData.json"; | ||
import feBomData from "./feBomData.json"; | ||
|
||
const getLicenseUrl = (licenseId: string | undefined): string | null => { | ||
if (!licenseId) return null; | ||
return licenseUrls[licenseId as keyof typeof licenseUrls] || null; | ||
}; | ||
interface CycloneDXExternalRef { | ||
url?: string; | ||
type?: string; | ||
comment?: string; | ||
} | ||
|
||
interface CycloneDXLicense { | ||
license?: { | ||
id?: string; | ||
}; | ||
} | ||
|
||
interface CycloneDXProperties { | ||
name?: string; | ||
value?: string; | ||
} | ||
|
||
interface CycloneDXComponent { | ||
type?: string; | ||
name?: string; | ||
group?: string; | ||
version?: string; | ||
bomRef?: string; | ||
author?: string; | ||
description?: string; | ||
licenses?: CycloneDXLicense[]; | ||
externalReferences?: CycloneDXExternalRef[]; | ||
properties?: CycloneDXProperties[]; | ||
} | ||
|
||
interface CycloneDXTool { | ||
name?: string; | ||
version?: string; | ||
vendor?: string; | ||
externalReferences?: CycloneDXExternalRef[]; | ||
} | ||
|
||
interface CycloneDXBOM { | ||
bomFormat?: string; | ||
specVersion?: string; | ||
version?: number; | ||
serialNumber?: string; | ||
metadata?: { | ||
timestamp?: string; | ||
tools?: CycloneDXTool[]; | ||
component?: CycloneDXComponent; | ||
}; | ||
components?: CycloneDXComponent[]; | ||
} | ||
|
||
const BOMDisplay: React.FC = () => { | ||
const { t } = useTranslation(); | ||
const [copyStatus, setCopyStatus] = useState(false); | ||
const [showExternalRefs, setShowExternalRefs] = useState<number | null>(null); | ||
const [activeTab, setActiveTab] = useState<string>("bom"); | ||
|
||
const bomData = activeTab === "bom" ? feBomData : beBomData; | ||
|
||
const handleCopy = () => { | ||
setCopyStatus(true); | ||
setTimeout(() => setCopyStatus(false), 2000); | ||
}; | ||
|
||
const bomData = (activeTab === "bom" ? feBomData : beBomData) as CycloneDXBOM; | ||
const packages = bomData?.sbom?.packages || []; | ||
Comment on lines
+25
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add TypeScript interfaces for the GitHub SBOM format. The code lacks type definitions for the new GitHub SBOM format, which could lead to runtime errors. Add these type definitions: interface GitHubSBOM {
sbom: {
spdxVersion: string;
creationInfo: {
created: string;
};
packages: Array<{
name: string;
versionInfo?: string;
licenseConcluded?: string;
externalRefs?: Array<{
referenceLocator?: string;
referenceCategory?: string;
}>;
}>;
};
}
const bomData = (activeTab === "bom" ? feBomData : beBomData) as GitHubSBOM;
const packages = bomData?.sbom?.packages || []; |
||
|
||
return ( | ||
<div className="p-4"> | ||
|
@@ -84,73 +40,68 @@ const BOMDisplay: React.FC = () => { | |
}`} | ||
onClick={() => setActiveTab("bom")} | ||
> | ||
Care Frontend | ||
{t("Care Frontend")} | ||
</button> | ||
<button | ||
className={`text-md w-full rounded-md px-4 py-2 transition-all duration-300 md:w-auto ${ | ||
activeTab === "beBom" ? "bg-primary text-white" : "bg-gray-200" | ||
}`} | ||
onClick={() => setActiveTab("beBom")} | ||
> | ||
Care Backend | ||
{t("Care Backend")} | ||
</button> | ||
</div> | ||
<Card className="rounded-lg bg-white p-4 shadow-md transition-all duration-300"> | ||
<div className="mb-4"> | ||
<h2 className="mb-2 text-xl font-semibold text-primary md:text-2xl"> | ||
{bomData.bomFormat || "N/A"} BOM (Version:{" "} | ||
{bomData.version || "N/A"}) | ||
{t("SPDX SBOM (Version: {{version}})", { | ||
version: bomData?.sbom?.spdxVersion || t("N/A"), | ||
})} | ||
</h2> | ||
<p className="text-sm text-gray-500"> | ||
Created on:{" "} | ||
{bomData.metadata?.timestamp | ||
? dayjs(bomData.metadata.timestamp).format("MMMM D, YYYY") | ||
: "N/A"} | ||
{t("Created on: {{date}}", { | ||
date: bomData?.sbom?.creationInfo?.created | ||
? dayjs(bomData.sbom.creationInfo.created).format( | ||
"MMMM D, YYYY", | ||
) | ||
: t("N/A"), | ||
})} | ||
</p> | ||
</div> | ||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2"> | ||
<h3 className="col-span-full text-lg font-semibold text-primary"> | ||
Components: | ||
{t("Packages:")} | ||
</h3> | ||
{bomData.components?.map((component, index) => ( | ||
{packages.map((pkg, index) => ( | ||
<div | ||
key={index} | ||
className="block rounded-md border p-2 transition-all duration-300 hover:shadow-lg" | ||
> | ||
<a | ||
href={ | ||
component.externalReferences?.[1]?.url || | ||
component.externalReferences?.[0]?.url || | ||
"#" | ||
} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
className="hover:text-primary-dark block text-primary" | ||
> | ||
<strong className="text-lg"> | ||
{component.name || "N/A"} v{component.version || "N/A"} | ||
{t("{{name}} v{{version}}", { | ||
name: pkg.name || t("N/A"), | ||
version: pkg.versionInfo || t("N/A"), | ||
})} | ||
</strong> | ||
</a> | ||
{component.licenses && component.licenses[0]?.license?.id && ( | ||
{pkg.licenseConcluded && ( | ||
<p className="text-base"> | ||
License:{" "} | ||
{t("License:")}{" "} | ||
<a | ||
href={ | ||
getLicenseUrl(component.licenses[0].license.id) || "#" | ||
} | ||
href={getLicenseUrl(pkg.licenseConcluded) || "#"} | ||
target="_blank" | ||
rel="noopener noreferrer" | ||
className="hover:text-primary-dark text-primary" | ||
> | ||
{component.licenses[0].license.id || "N/A"} | ||
{pkg.licenseConcluded || t("N/A")} | ||
</a> | ||
</p> | ||
)} | ||
{component.description && ( | ||
<p className="text-base"> | ||
Description: {component.description} | ||
</p> | ||
)} | ||
<div> | ||
<h4 | ||
className="block cursor-pointer font-semibold text-primary" | ||
|
@@ -164,15 +115,21 @@ const BOMDisplay: React.FC = () => { | |
</h4> | ||
{showExternalRefs === index && ( | ||
<ul className="list-inside list-disc pl-4 text-xs"> | ||
{component.externalReferences?.map((ref, idx) => ( | ||
{pkg.externalRefs?.map((ref, idx) => ( | ||
<li key={idx}> | ||
<a | ||
href={ref.url || "#"} | ||
href={ref.referenceLocator || "#"} | ||
className="hover:text-primary-dark block break-words text-primary" | ||
> | ||
{ref.url || "N/A"} | ||
{ref.referenceLocator || t("N/A")} | ||
</a> | ||
{ref.comment && <p>Comment: {ref.comment}</p>} | ||
{ref.referenceCategory && ( | ||
<p> | ||
{t("Category: {{category}}", { | ||
category: ref.referenceCategory, | ||
})} | ||
</p> | ||
)} | ||
</li> | ||
))} | ||
</ul> | ||
|
@@ -187,12 +144,12 @@ const BOMDisplay: React.FC = () => { | |
onCopy={handleCopy} | ||
> | ||
<button className="text-md hover:bg-primary-dark w-full rounded-md bg-primary px-4 py-2 text-white transition-all duration-300 focus:outline-none md:w-auto"> | ||
Copy BOM JSON | ||
{t("Copy BOM JSON")} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is not the way to add translations. see how other translations are done |
||
</button> | ||
</CopyToClipboard> | ||
{copyStatus && ( | ||
<span className="mt-2 block text-sm text-gray-600"> | ||
Copied to clipboard! | ||
{t("Copied to clipboard!")} | ||
</span> | ||
)} | ||
</div> | ||
|
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.
let's keep this inside the
public
dir. instead.(we'd also be able to access it by
care.ohc.network/licenses/backend|frontend.json
)