mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-26 16:46:56 +00:00
fix: improve host diagnostics, storage handling, and maintenance workflows
- add zero-downtime Proxmox TLS certificate refresh from the Security panel (#307) - classify storage availability independently from missing capacity information (#309) - update ZFS ARC sizing and safely reconcile conflicting module configurations - preserve and restore migrated ZFS settings without overwriting later administrator changes - stop memory optimization from forcing the kernel overcommit policy - correlate multi-line OOM events and identify the affected LXC, cgroup limits, swap and killed process - add selectable Bash prompt path styles and clearer shell activation guidance - protect technical names during automatic translation and correct localized terminology - update the Coral and VM/LXC Apps and Updates documentation, translations and screenshots
This commit is contained in:
@@ -41,6 +41,7 @@ export default async function InstallCoralTPUHostPage({
|
||||
pcie: { items: StringItem[]; kernelPatches: StringItem[]; afterItems: StringItem[] }
|
||||
usb: { items: StringItem[] }
|
||||
}
|
||||
legacyCleanup: { items: StringItem[] }
|
||||
reinstallUninstall: { uninstallItems: StringItem[] }
|
||||
related: { items: RelatedItem[] }
|
||||
} } }
|
||||
@@ -50,6 +51,7 @@ export default async function InstallCoralTPUHostPage({
|
||||
const kernelPatches = messages.docs.hardware.installCoralTpuHost.walkthrough.pcie.kernelPatches
|
||||
const pcieAfterItems = messages.docs.hardware.installCoralTpuHost.walkthrough.pcie.afterItems
|
||||
const usbItems = messages.docs.hardware.installCoralTpuHost.walkthrough.usb.items
|
||||
const legacyCleanupItems = messages.docs.hardware.installCoralTpuHost.legacyCleanup.items
|
||||
const uninstallItems = messages.docs.hardware.installCoralTpuHost.reinstallUninstall.uninstallItems
|
||||
const relatedItems = messages.docs.hardware.installCoralTpuHost.related.items
|
||||
|
||||
@@ -165,54 +167,18 @@ export default async function InstallCoralTPUHostPage({
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">{t("howRuns.body")}</p>
|
||||
|
||||
<pre className="bg-gray-100 text-gray-800 p-4 rounded-md overflow-x-auto text-sm my-4 border border-gray-200 leading-snug">
|
||||
{`┌────────────────────────────────────────────────┐
|
||||
│ 1. detect_coral_hardware() │
|
||||
│ → count PCIe (vendor 1ac1) + USB (IDs) │
|
||||
└────────────────┬───────────────────────────────┘
|
||||
▼
|
||||
┌───────────┴───────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
None At least one
|
||||
│ │
|
||||
▼ ▼
|
||||
Dialog pre_install_prompt()
|
||||
"No Coral" → shows what was detected
|
||||
exit 0 and what will be installed
|
||||
│
|
||||
▼
|
||||
┌────────────────┴────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
PCIe detected? USB detected?
|
||||
│ │
|
||||
Yes Yes
|
||||
▼ ▼
|
||||
install_gasket_apex_dkms install_libedgetpu_runtime
|
||||
├─ cleanup_broken_gasket_dkms ├─ add Google GPG keyring
|
||||
├─ apt install deps │ /etc/apt/keyrings/...
|
||||
│ (git, dkms, build-essential, ├─ add APT repo (signed-by)
|
||||
│ proxmox-headers-$(uname-r)) │ /etc/apt/sources.list.d/
|
||||
├─ clone feranick/gasket-driver │ coral-edgetpu.list
|
||||
│ (google fallback + patches) ├─ apt install libedgetpu1-std
|
||||
├─ copy src/ → /usr/src/ └─ udev reload + trigger
|
||||
│ gasket-1.0/
|
||||
├─ generate dkms.conf
|
||||
├─ dkms add / build / install
|
||||
└─ modprobe gasket + apex
|
||||
+ ensure_apex_group_and_udev
|
||||
│ │
|
||||
└────────────────┬────────────────┘
|
||||
▼
|
||||
┌────────────────┴────────────────┐
|
||||
│ │
|
||||
PCIe ran? USB only
|
||||
│ │
|
||||
▼ ▼
|
||||
restart_prompt() "No reboot required"
|
||||
(reboot required to (runtime + udev rules
|
||||
load fresh kernel are already active)
|
||||
module cleanly)`}
|
||||
{`Detect Coral hardware
|
||||
│
|
||||
├─ PCIe / M.2 detected ──► build gasket + apex with DKMS
|
||||
│ └─ reboot recommended
|
||||
│
|
||||
├─ USB detected ─────────► install libedgetpu runtime
|
||||
│ └─ no reboot required
|
||||
│
|
||||
└─ no PCIe / M.2 detected
|
||||
├─ no legacy gasket-dkms state ─► no PCIe changes
|
||||
└─ legacy gasket-dkms state ────► offer optional cleanup
|
||||
└─ USB runtime untouched`}
|
||||
</pre>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("walkthrough.heading")}</h2>
|
||||
@@ -290,6 +256,19 @@ export default async function InstallCoralTPUHostPage({
|
||||
</Steps.Step>
|
||||
</Steps>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("legacyCleanup.heading")}</h2>
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
{t.rich("legacyCleanup.intro", { code, strong })}
|
||||
</p>
|
||||
<ul className="list-disc pl-6 mb-4 text-gray-800 leading-relaxed space-y-1">
|
||||
{legacyCleanupItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`legacyCleanup.items.${idx}`, { code, strong })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<Callout variant="warning" title={t("legacyCleanup.warningTitle")}>
|
||||
{t("legacyCleanup.warningBody")}
|
||||
</Callout>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("reinstallUninstall.heading")}</h2>
|
||||
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Metadata } from "next"
|
||||
import type React from "react"
|
||||
import { getTranslations, getMessages, setRequestLocale } from "next-intl/server"
|
||||
import { getMessages, getTranslations, setRequestLocale } from "next-intl/server"
|
||||
import { Link } from "@/i18n/navigation"
|
||||
import { DocHeader } from "@/components/ui/doc-header"
|
||||
import { Callout } from "@/components/ui/callout"
|
||||
import CopyableCode from "@/components/CopyableCode"
|
||||
import { DocHeader } from "@/components/ui/doc-header"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
@@ -16,21 +16,15 @@ export async function generateMetadata({
|
||||
return { title: t("title"), description: t("description") }
|
||||
}
|
||||
|
||||
type TableRow = { method: string; when: string }
|
||||
type BreakdownRow = { part: string; meaning: string }
|
||||
type ExampleRow = { text: string; regex: string; result: string }
|
||||
type DetectorRow = { method: string; use: string }
|
||||
type StateRow = { state: string; display: string; meaning: string }
|
||||
type ProblemRow = { problem: string; resolution: string }
|
||||
|
||||
function Figure({ src, alt, caption }: { src: string; alt: string; caption: string }) {
|
||||
return (
|
||||
<figure className="my-6">
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="rounded-lg border border-gray-200 shadow-sm w-full"
|
||||
/>
|
||||
<figcaption className="text-sm text-gray-500 mt-2 text-center italic">
|
||||
{caption}
|
||||
</figcaption>
|
||||
<img src={src} alt={alt} className="w-full rounded-lg border border-gray-200 shadow-sm" />
|
||||
<figcaption className="mt-2 text-center text-sm italic text-gray-500">{caption}</figcaption>
|
||||
</figure>
|
||||
)
|
||||
}
|
||||
@@ -43,362 +37,178 @@ export default async function AppTabPage({
|
||||
const { locale } = await params
|
||||
setRequestLocale(locale)
|
||||
const t = await getTranslations({ locale, namespace: "docs.monitor.dashboard.vmsLxcsApp" })
|
||||
|
||||
const messages = (await getMessages({ locale })) as unknown as {
|
||||
docs: { monitor: { dashboard: { vmsLxcsApp: {
|
||||
whatYouGet: { items: string[] }
|
||||
registerSuggested: { steps: string[] }
|
||||
manual: { linksItems: string[] }
|
||||
multiple: { usefulItems: string[] }
|
||||
tracking: {
|
||||
ingredients: string[]
|
||||
methodsTable: { rows: TableRow[] }
|
||||
sourceItems: string[]
|
||||
regexTwoItems: string[]
|
||||
step2Items: string[]
|
||||
step2Breakdown: { rows: BreakdownRow[] }
|
||||
step3Examples: { rows: ExampleRow[] }
|
||||
step4Items: string[]
|
||||
step6CorrectItems: string[]
|
||||
}
|
||||
state: { items: string[] }
|
||||
manage: { items: string[] }
|
||||
options: { items: string[] }
|
||||
notDetected: { steps: string[] }
|
||||
overview: { items: string[] }
|
||||
discovery: { items: string[] }
|
||||
registration: { steps: string[] }
|
||||
catalog: { items: string[] }
|
||||
docker: { items: string[] }
|
||||
webLinks: { items: string[] }
|
||||
tracking: { detectorRows: DetectorRow[]; sources: string[]; regexRules: string[] }
|
||||
updater: { items: string[] }
|
||||
states: { rows: StateRow[] }
|
||||
management: { items: string[] }
|
||||
troubleshooting: { rows: ProblemRow[] }
|
||||
} } } }
|
||||
}
|
||||
const v = messages.docs.monitor.dashboard.vmsLxcsApp
|
||||
const whatYouGetItems = v.whatYouGet.items
|
||||
const registerSteps = v.registerSuggested.steps
|
||||
const manualLinks = v.manual.linksItems
|
||||
const multipleUseful = v.multiple.usefulItems
|
||||
const ingredients = v.tracking.ingredients
|
||||
const methodsRows = v.tracking.methodsTable.rows
|
||||
const sourceItems = v.tracking.sourceItems
|
||||
const regexTwoItems = v.tracking.regexTwoItems
|
||||
const step2Items = v.tracking.step2Items
|
||||
const breakdownRows = v.tracking.step2Breakdown.rows
|
||||
const exampleRows = v.tracking.step3Examples.rows
|
||||
const step4Items = v.tracking.step4Items
|
||||
const step6CorrectItems = v.tracking.step6CorrectItems
|
||||
const stateItems = v.state.items
|
||||
const manageItems = v.manage.items
|
||||
const optionsItems = v.options.items
|
||||
const notDetectedSteps = v.notDetected.steps
|
||||
|
||||
// Rich-text tag handlers
|
||||
const strong = (chunks: React.ReactNode) => <strong>{chunks}</strong>
|
||||
const em = (chunks: React.ReactNode) => <em>{chunks}</em>
|
||||
const code = (chunks: React.ReactNode) => (
|
||||
<code className="text-sm bg-gray-100 px-1 rounded">{chunks}</code>
|
||||
)
|
||||
const code = (chunks: React.ReactNode) => <code className="rounded bg-gray-100 px-1 text-sm">{chunks}</code>
|
||||
const linkUpdates = (chunks: React.ReactNode) => (
|
||||
<Link href="/docs/monitor/dashboard/vms-lxcs/updates" className="text-blue-600 hover:underline">
|
||||
{chunks}
|
||||
</Link>
|
||||
)
|
||||
const richList = (base: string, items: string[]) => (
|
||||
<ul className="mt-2 list-disc space-y-2 pl-6 text-gray-800">
|
||||
{items.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`${base}.${idx}`, { strong, em, code, link: linkUpdates })}</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
<DocHeader
|
||||
title={t("header.title")}
|
||||
description={t("header.description")}
|
||||
estimatedMinutes={12}
|
||||
/>
|
||||
<div className="mx-auto max-w-4xl px-4 py-8">
|
||||
<DocHeader title={t("header.title")} description={t("header.description")} estimatedMinutes={11} />
|
||||
|
||||
<p className="text-gray-800 mt-6">{t.rich("intro.p1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("intro.p2", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("intro.p3", { strong, em, code, link: linkUpdates })}</p>
|
||||
<p className="mt-6 text-gray-800">{t.rich("intro.p1", { strong, em, code })}</p>
|
||||
<p className="mt-4 text-gray-800">{t.rich("intro.p2", { strong, em, code, link: linkUpdates })}</p>
|
||||
<Callout variant="info">{t.rich("intro.callout", { strong, em, code })}</Callout>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("whatYouGet.heading")}</h2>
|
||||
<p className="text-gray-800">{t("whatYouGet.lead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{whatYouGetItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`whatYouGet.items.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("whatYouGet.trailing", { strong, em, code })}</p>
|
||||
<Callout variant="warning">{t.rich("whatYouGet.callout", { strong, em, code })}</Callout>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("overview.heading")}</h2>
|
||||
<p className="text-gray-800">{t("overview.lead")}</p>
|
||||
{richList("overview.items", v.overview.items)}
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("firstOpening.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("firstOpening.p1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("firstOpening.p2", { strong, em, code })}</p>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("discovery.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("discovery.lead", { strong, em, code })}</p>
|
||||
{richList("discovery.items", v.discovery.items)}
|
||||
<Callout variant="tip">{t.rich("discovery.callout", { strong, em, code })}</Callout>
|
||||
|
||||
<Figure
|
||||
src="/monitor/vms-modal-app-01.png"
|
||||
alt={t("figures.f01.alt")}
|
||||
caption={t("figures.f01.caption")}
|
||||
/>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("registerSuggested.heading")}</h3>
|
||||
<ol className="list-decimal pl-6 space-y-1 text-gray-800">
|
||||
{registerSteps.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`registerSuggested.steps.${idx}`, { strong, em, code })}</li>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("registration.heading")}</h2>
|
||||
<p className="text-gray-800">{t("registration.lead")}</p>
|
||||
<ol className="mt-2 list-decimal space-y-2 pl-6 text-gray-800">
|
||||
{v.registration.steps.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`registration.steps.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ol>
|
||||
<p className="text-gray-800 mt-4">{t.rich("registerSuggested.trailing", { strong, em, code })}</p>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("catalog.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("catalog.p1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("catalog.p2", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("catalog.p3", { strong, em, code })}</p>
|
||||
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("catalog.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("catalog.lead", { strong, em, code })}</p>
|
||||
{richList("catalog.items", v.catalog.items)}
|
||||
<Figure
|
||||
src="/monitor/vms-modal-app-02.png"
|
||||
alt={t("figures.f02.alt")}
|
||||
caption={t("figures.f02.caption")}
|
||||
alt={t("figures.catalog.alt")}
|
||||
caption={t("figures.catalog.caption")}
|
||||
/>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("manual.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("manual.p1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("manual.p2", { strong, em, code })}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("manual.nameHeading")}</h3>
|
||||
<p className="text-gray-800">{t.rich("manual.nameBody", { strong, em, code })}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("manual.linksHeading")}</h3>
|
||||
<p className="text-gray-800">{t("manual.linksLead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{manualLinks.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`manual.linksItems.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("manual.linksTrailing", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("manual.linksConfirm", { strong, em, code })}</p>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("docker.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("docker.lead", { strong, em, code })}</p>
|
||||
{richList("docker.items", v.docker.items)}
|
||||
<Callout variant="info">{t.rich("docker.callout", { strong, em, code })}</Callout>
|
||||
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("webLinks.heading")}</h2>
|
||||
<p className="text-gray-800">{t("webLinks.lead")}</p>
|
||||
{richList("webLinks.items", v.webLinks.items)}
|
||||
<Figure
|
||||
src="/monitor/vms-modal-app-03.png"
|
||||
alt={t("figures.f03.alt")}
|
||||
caption={t("figures.f03.caption")}
|
||||
alt={t("figures.webLinks.alt")}
|
||||
caption={t("figures.webLinks.caption")}
|
||||
/>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("multiple.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("multiple.intro", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("multiple.usefulLead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{multipleUseful.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`multiple.usefulItems.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("multiple.dontGroup", { strong, em, code })}</p>
|
||||
|
||||
<Figure
|
||||
src="/monitor/vms-modal-app-04.png"
|
||||
alt={t("figures.f04.alt")}
|
||||
caption={t("figures.f04.caption")}
|
||||
/>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("tracking.heading")}</h2>
|
||||
<p className="text-gray-800">{t("tracking.intro")}</p>
|
||||
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{ingredients.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`tracking.ingredients.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ol>
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.trailing", { strong, em, code })}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("tracking.methodsHeading")}</h3>
|
||||
<p className="text-gray-800">{t("tracking.methodsLead")}</p>
|
||||
<div className="overflow-x-auto my-4">
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("tracking.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("tracking.lead", { strong, em, code })}</p>
|
||||
<div className="my-4 overflow-x-auto">
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.methodsTable.colMethod")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.methodsTable.colWhen")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.colMethod")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.colUse")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{methodsRows.map((row, idx) => (
|
||||
{v.tracking.detectorRows.map((row, idx) => (
|
||||
<tr key={idx}>
|
||||
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.method}</td>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.when}</td>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.use}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.methodsTrailing", { strong, em, code })}</p>
|
||||
|
||||
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.commandHeading")}</h4>
|
||||
<p className="text-gray-800">{t.rich("tracking.commandP1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("tracking.commandP2")}</p>
|
||||
<CopyableCode code={t("tracking.commandExample1")} language="text" />
|
||||
<p className="text-gray-800 mt-4">{t("tracking.commandP3")}</p>
|
||||
<CopyableCode code={t("tracking.commandExample2")} language="text" />
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.commandP4", { strong, em, code })}</p>
|
||||
<h3 className="mt-8 mb-2 text-lg font-semibold text-gray-900">{t("tracking.sourcesHeading")}</h3>
|
||||
{richList("tracking.sources", v.tracking.sources)}
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("tracking.sourceHeading")}</h3>
|
||||
<p className="text-gray-800">{t("tracking.sourceLead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{sourceItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`tracking.sourceItems.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.sourceTrailing", { strong, em, code })}</p>
|
||||
<h3 className="mt-8 mb-2 text-lg font-semibold text-gray-900">{t("tracking.regexHeading")}</h3>
|
||||
<p className="text-gray-800">{t.rich("tracking.regexLead", { strong, em, code })}</p>
|
||||
{richList("tracking.regexRules", v.tracking.regexRules)}
|
||||
<p className="mt-4 text-gray-800">{t("tracking.regexExampleLead")}</p>
|
||||
<CopyableCode code={t.raw("tracking.regexExample") as string} language="text" />
|
||||
<Callout variant="warning">{t.rich("tracking.regexCallout", { strong, em, code })}</Callout>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("tracking.regexHeading")}</h3>
|
||||
<p className="text-gray-800">{t.rich("tracking.regexIntro", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.regexOptional", { strong, em, code })}</p>
|
||||
<Figure
|
||||
src="/monitor/vms-modal-app-05.png"
|
||||
alt={t("figures.tracking.alt")}
|
||||
caption={t("figures.tracking.caption")}
|
||||
/>
|
||||
|
||||
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.regexTwoHeading")}</h4>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{regexTwoItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`tracking.regexTwoItems.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.regexTwoTrailing", { strong, em, code })}</p>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("updater.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("updater.lead", { strong, em, code, link: linkUpdates })}</p>
|
||||
{richList("updater.items", v.updater.items)}
|
||||
|
||||
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step1Heading")}</h4>
|
||||
<p className="text-gray-800">{t("tracking.step1P1")}</p>
|
||||
<p className="text-gray-800 mt-4">{t("tracking.step1P2")}</p>
|
||||
<p className="text-gray-800 mt-4">{t("tracking.step1P3")}</p>
|
||||
<CopyableCode code={t("tracking.step1Cmd")} language="sh" />
|
||||
<p className="text-gray-800 mt-4">{t("tracking.step1P4")}</p>
|
||||
<CopyableCode code={t("tracking.step1Output")} language="text" />
|
||||
<p className="text-gray-800 mt-4">{t("tracking.step1P5")}</p>
|
||||
<p className="text-gray-800 mt-4">{t("tracking.step1P6")}</p>
|
||||
|
||||
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step2Heading")}</h4>
|
||||
<p className="text-gray-800">{t.rich("tracking.step2Lead", { strong, em, code })}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{step2Items.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`tracking.step2Items.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t("tracking.step2Recommended")}</p>
|
||||
<CopyableCode code={t("tracking.step2Regex")} language="text" />
|
||||
<p className="text-gray-800 mt-4">{t("tracking.step2ReadLead")}</p>
|
||||
<div className="overflow-x-auto my-4">
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("states.heading")}</h2>
|
||||
<div className="my-4 overflow-x-auto">
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.step2Breakdown.colPart")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.step2Breakdown.colMeaning")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("states.colState")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("states.colDisplay")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("states.colMeaning")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{breakdownRows.map((row, idx) => (
|
||||
{v.states.rows.map((row, idx) => (
|
||||
<tr key={idx}>
|
||||
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.part}</td>
|
||||
<td className="border border-gray-300 px-3 py-2 font-medium">{row.state}</td>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.display}</td>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.meaning}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.step2DotNote", { strong, em, code })}</p>
|
||||
<Figure
|
||||
src="/monitor/vms-modal-app-06.png"
|
||||
alt={t("figures.card.alt")}
|
||||
caption={t("figures.card.caption")}
|
||||
/>
|
||||
|
||||
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step3Heading")}</h4>
|
||||
<p className="text-gray-800">{t("tracking.step3Lead")}</p>
|
||||
<div className="overflow-x-auto my-4">
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("management.heading")}</h2>
|
||||
<p className="text-gray-800">{t("management.lead")}</p>
|
||||
{richList("management.items", v.management.items)}
|
||||
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("troubleshooting.heading")}</h2>
|
||||
<div className="my-4 overflow-x-auto">
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.step3Examples.colText")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.step3Examples.colRegex")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("tracking.step3Examples.colResult")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("troubleshooting.colProblem")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("troubleshooting.colResolution")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{exampleRows.map((row, idx) => (
|
||||
{v.troubleshooting.rows.map((row, idx) => (
|
||||
<tr key={idx}>
|
||||
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.text}</td>
|
||||
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.regex}</td>
|
||||
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.result}</td>
|
||||
<td className="border border-gray-300 px-3 py-2 font-medium">{row.problem}</td>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.resolution}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.step3Note1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.step3Note2", { strong, em, code })}</p>
|
||||
|
||||
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step4Heading")}</h4>
|
||||
<p className="text-gray-800">{t("tracking.step4Intro")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{step4Items.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`tracking.step4Items.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.step4Trailing", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4"><strong>{t("tracking.step4RecLabel")}</strong></p>
|
||||
<CopyableCode code={t.raw("tracking.step4RecRegex") as string} language="text" />
|
||||
<p className="text-gray-800 mt-4"><strong>{t("tracking.step4LessLabel")}</strong></p>
|
||||
<CopyableCode code={t("tracking.step4LessRegex")} language="text" />
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.step4Note", { strong, em, code })}</p>
|
||||
|
||||
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step5Heading")}</h4>
|
||||
<p className="text-gray-800">{t("tracking.step5Lead")}</p>
|
||||
<CopyableCode code={t("tracking.step5Regex")} language="text" />
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.step5P1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("tracking.step5P2")}</p>
|
||||
|
||||
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("tracking.step6Heading")}</h4>
|
||||
<p className="text-gray-800">{t.rich("tracking.step6Lead", { strong, em, code })}</p>
|
||||
<CopyableCode code={t("tracking.step6Output")} language="text" />
|
||||
<p className="text-gray-800 mt-4">{t("tracking.step6CorrectLead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{step6CorrectItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`tracking.step6CorrectItems.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("tracking.step6ErrorNote", { strong, em, code })}</p>
|
||||
<Callout variant="tip">{t.rich("tracking.step6Callout", { strong, em, code })}</Callout>
|
||||
|
||||
<Figure
|
||||
src="/monitor/vms-modal-app-05.png"
|
||||
alt={t("figures.f05.alt")}
|
||||
caption={t("figures.f05.caption")}
|
||||
/>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("state.heading")}</h2>
|
||||
|
||||
<Figure
|
||||
src="/monitor/vms-modal-app-06.png"
|
||||
alt={t("figures.f06.alt")}
|
||||
caption={t("figures.f06.caption")}
|
||||
/>
|
||||
|
||||
<p className="text-gray-800">{t("state.lead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{stateItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`state.items.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("state.trailing", { strong, em, code })}</p>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("manage.heading")}</h2>
|
||||
<p className="text-gray-800">{t("manage.lead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{manageItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`manage.items.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("manage.trailing", { strong, em, code })}</p>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("options.heading")}</h2>
|
||||
<p className="text-gray-800">{t("options.lead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{optionsItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`options.items.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("options.trailing", { strong, em, code })}</p>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("notDetected.heading")}</h2>
|
||||
<p className="text-gray-800">{t("notDetected.intro")}</p>
|
||||
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{notDetectedSteps.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`notDetected.steps.${idx}`, { strong, em, code, link: linkUpdates })}</li>
|
||||
))}
|
||||
</ol>
|
||||
<p className="text-gray-800 mt-4">{t.rich("notDetected.trailing", { strong, em, code })}</p>
|
||||
|
||||
<Figure
|
||||
src="/monitor/vms-modal-app-07.png"
|
||||
alt={t("figures.f07.alt")}
|
||||
caption={t("figures.f07.caption")}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -225,38 +225,34 @@ export default async function VmsLxcsTabPage({
|
||||
<h4 className="text-base font-semibold mt-6 mb-2 text-gray-900">{t("drillIn.ipsTitle")}</h4>
|
||||
<p className="mb-6 text-gray-800 leading-relaxed">{t("drillIn.ipsBody")}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">App</h3>
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("drillIn.appTitle")}</h3>
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
The <strong>App</strong> section lets you register the applications running inside an LXC, wire quick web
|
||||
links, and optionally track installed vs. upstream versions. Registrations feed the App-level notifications
|
||||
and the update flows on the next section.
|
||||
{t.rich("drillIn.appIntro", { strong, em, code })}
|
||||
</p>
|
||||
<p className="mb-6 text-gray-800 leading-relaxed">
|
||||
See the{" "}
|
||||
{t("drillIn.appLinkLead")}{" "}
|
||||
<Link
|
||||
href="/docs/monitor/dashboard/vms-lxcs/app"
|
||||
className="text-blue-600 hover:underline"
|
||||
>
|
||||
dedicated App page
|
||||
{t("drillIn.appLinkLabel")}
|
||||
</Link>{" "}
|
||||
for the catalog, manual registration, version-tracking methods and regex patterns.
|
||||
{t("drillIn.appLinkTail")}
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">Updates</h3>
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("drillIn.updatesTitle")}</h3>
|
||||
<p className="mb-4 text-gray-800 leading-relaxed">
|
||||
The <strong>Updates</strong> section covers OS package updates (APT / APK) and application updates via
|
||||
Community Scripts helpers or custom commands. Detection runs unconditionally on running LXCs; whether pending
|
||||
updates also trigger a notification is controlled from <strong>Settings → Notifications</strong>.
|
||||
{t.rich("drillIn.updatesIntro", { strong, em, code })}
|
||||
</p>
|
||||
<p className="mb-6 text-gray-800 leading-relaxed">
|
||||
See the{" "}
|
||||
{t("drillIn.updatesLinkLead")}{" "}
|
||||
<Link
|
||||
href="/docs/monitor/dashboard/vms-lxcs/updates"
|
||||
className="text-blue-600 hover:underline"
|
||||
>
|
||||
dedicated Updates page
|
||||
{t("drillIn.updatesLinkLabel")}
|
||||
</Link>{" "}
|
||||
for the decision matrix, custom-command guidance, backup / restart preferences and scheduled updates.
|
||||
{t("drillIn.updatesLinkTail")}
|
||||
</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("drillIn.mountsTitle")}</h3>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { Metadata } from "next"
|
||||
import type React from "react"
|
||||
import { getTranslations, getMessages, setRequestLocale } from "next-intl/server"
|
||||
import { Link } from "@/i18n/navigation"
|
||||
import { getMessages, getTranslations, setRequestLocale } from "next-intl/server"
|
||||
import { ExternalLink } from "lucide-react"
|
||||
import { DocHeader } from "@/components/ui/doc-header"
|
||||
import { Link } from "@/i18n/navigation"
|
||||
import { Callout } from "@/components/ui/callout"
|
||||
import CopyableCode from "@/components/CopyableCode"
|
||||
import { DocHeader } from "@/components/ui/doc-header"
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
@@ -17,20 +17,15 @@ export async function generateMetadata({
|
||||
return { title: t("title"), description: t("description") }
|
||||
}
|
||||
|
||||
type DecisionRow = { situation: string; action: string }
|
||||
type DifferenceRow = { field: string; location: string; role: string }
|
||||
type MechanismRow = { source: string; action: string; notes: string }
|
||||
type StatusRow = { state: string; appearance: string; meaning: string }
|
||||
type ProblemRow = { problem: string; resolution: string }
|
||||
|
||||
function Figure({ src, alt, caption }: { src: string; alt: string; caption: string }) {
|
||||
return (
|
||||
<figure className="my-6">
|
||||
<img
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="rounded-lg border border-gray-200 shadow-sm w-full"
|
||||
/>
|
||||
<figcaption className="text-sm text-gray-500 mt-2 text-center italic">
|
||||
{caption}
|
||||
</figcaption>
|
||||
<img src={src} alt={alt} className="w-full rounded-lg border border-gray-200 shadow-sm" />
|
||||
<figcaption className="mt-2 text-center text-sm italic text-gray-500">{caption}</figcaption>
|
||||
</figure>
|
||||
)
|
||||
}
|
||||
@@ -43,58 +38,31 @@ export default async function UpdatesTabPage({
|
||||
const { locale } = await params
|
||||
setRequestLocale(locale)
|
||||
const t = await getTranslations({ locale, namespace: "docs.monitor.dashboard.vmsLxcsUpdates" })
|
||||
|
||||
const messages = (await getMessages({ locale })) as unknown as {
|
||||
docs: { monitor: { dashboard: { vmsLxcsUpdates: {
|
||||
decision: { table: { rows: DecisionRow[] } }
|
||||
figureOut: {
|
||||
step3Items: string[]
|
||||
step4Items: string[]
|
||||
}
|
||||
requirements: { items: string[] }
|
||||
difference: { table: { rows: DifferenceRow[] } }
|
||||
apply: {
|
||||
steps: string[]
|
||||
systemItems: string[]
|
||||
appItems: string[]
|
||||
}
|
||||
scheduled: { createSteps: string[] }
|
||||
overview: { items: string[] }
|
||||
mechanisms: { rows: MechanismRow[] }
|
||||
docker: { items: string[] }
|
||||
actions: { items: string[]; statusRows: StatusRow[] }
|
||||
custom: { items: string[] }
|
||||
bulk: { items: string[] }
|
||||
options: { items: string[] }
|
||||
scheduled: { items: string[] }
|
||||
completion: { items: string[] }
|
||||
troubleshooting: { rows: ProblemRow[] }
|
||||
} } } }
|
||||
}
|
||||
const v = messages.docs.monitor.dashboard.vmsLxcsUpdates
|
||||
const decisionRows = v.decision.table.rows
|
||||
const step3Items = v.figureOut.step3Items
|
||||
const step4Items = v.figureOut.step4Items
|
||||
const reqItems = v.requirements.items
|
||||
const diffRows = v.difference.table.rows
|
||||
const applySteps = v.apply.steps
|
||||
const applySystem = v.apply.systemItems
|
||||
const applyApp = v.apply.appItems
|
||||
const schedSteps = v.scheduled.createSteps
|
||||
|
||||
// Rich-text tag handlers
|
||||
const strong = (chunks: React.ReactNode) => <strong>{chunks}</strong>
|
||||
const em = (chunks: React.ReactNode) => <em>{chunks}</em>
|
||||
const code = (chunks: React.ReactNode) => (
|
||||
<code className="text-sm bg-gray-100 px-1 rounded">{chunks}</code>
|
||||
)
|
||||
const code = (chunks: React.ReactNode) => <code className="rounded bg-gray-100 px-1 text-sm">{chunks}</code>
|
||||
const linkApp = (chunks: React.ReactNode) => (
|
||||
<Link href="/docs/monitor/dashboard/vms-lxcs/app" className="text-blue-600 hover:underline">
|
||||
{chunks}
|
||||
</Link>
|
||||
)
|
||||
const linkHelperHome = (chunks: React.ReactNode) => (
|
||||
<a
|
||||
href="https://community-scripts.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1 text-blue-600 hover:underline"
|
||||
>
|
||||
{chunks}
|
||||
<ExternalLink className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
</a>
|
||||
)
|
||||
const linkHelperDocs = (chunks: React.ReactNode) => (
|
||||
const linkHelper = (chunks: React.ReactNode) => (
|
||||
<a
|
||||
href="https://community-scripts.org/docs/tools/pve/update-apps"
|
||||
target="_blank"
|
||||
@@ -106,244 +74,134 @@ export default async function UpdatesTabPage({
|
||||
</a>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
||||
<DocHeader
|
||||
title={t("header.title")}
|
||||
description={t("header.description")}
|
||||
estimatedMinutes={12}
|
||||
/>
|
||||
const richList = (base: string, items: string[]) => (
|
||||
<ul className="mt-2 list-disc space-y-2 pl-6 text-gray-800">
|
||||
{items.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`${base}.${idx}`, { strong, em, code, link: linkApp, helper: linkHelper })}</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
|
||||
<p className="text-gray-800 mt-6">{t.rich("intro.p1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("intro.p2", { strong, em, code, link: linkApp })}</p>
|
||||
return (
|
||||
<div className="mx-auto max-w-4xl px-4 py-8">
|
||||
<DocHeader title={t("header.title")} description={t("header.description")} estimatedMinutes={10} />
|
||||
|
||||
<p className="mt-6 text-gray-800">{t.rich("intro.p1", { strong, em, code, link: linkApp })}</p>
|
||||
<p className="mt-4 text-gray-800">{t.rich("intro.p2", { strong, em, code })}</p>
|
||||
<Callout variant="info">{t.rich("intro.callout", { strong, em, code })}</Callout>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("mechanisms.heading")}</h2>
|
||||
<p className="text-gray-800">{t("mechanisms.intro")}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("mechanisms.osHeading")}</h3>
|
||||
<p className="text-gray-800">{t("mechanisms.osP1")}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("mechanisms.osP2", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("mechanisms.osP3")}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("mechanisms.helperHeading")}</h3>
|
||||
<p className="text-gray-800">{t.rich("mechanisms.helperP1", { strong, em, code, linkHelperHome })}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("mechanisms.helperP2", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">
|
||||
{t.rich("mechanisms.helperP3", { strong, em, code, linkHelperHome, linkHelperDocs })}
|
||||
</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("mechanisms.helperP4", { strong, em, code })}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("mechanisms.customHeading")}</h3>
|
||||
<p className="text-gray-800">{t.rich("mechanisms.customP1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("mechanisms.customP2")}</p>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("decision.heading")}</h2>
|
||||
<div className="overflow-x-auto my-4">
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("decision.table.colSituation")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("decision.table.colAction")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{decisionRows.map((row, idx) => (
|
||||
<tr key={idx}>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.situation}</td>
|
||||
<td className="border border-gray-300 px-3 py-2 font-mono text-xs">{row.action}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p className="text-gray-800 mt-4">{t("decision.trailing")}</p>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("overview.heading")}</h2>
|
||||
<p className="text-gray-800">{t("overview.lead")}</p>
|
||||
{richList("overview.items", v.overview.items)}
|
||||
|
||||
<Figure
|
||||
src="/monitor/vms-modal-updates-01.png"
|
||||
alt={t("figures.f01.alt")}
|
||||
caption={t("figures.f01.caption")}
|
||||
/>
|
||||
<Figure
|
||||
src="/monitor/vms-modal-updates-02.png"
|
||||
alt={t("figures.f02.alt")}
|
||||
caption={t("figures.f02.caption")}
|
||||
alt={t("figures.osPending.alt")}
|
||||
caption={t("figures.osPending.caption")}
|
||||
/>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("custom.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("custom.p1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("custom.p2")}</p>
|
||||
|
||||
<Figure
|
||||
src="/monitor/vms-modal-updates-03.png"
|
||||
alt={t("figures.f03.alt")}
|
||||
caption={t("figures.f03.caption")}
|
||||
/>
|
||||
<Figure
|
||||
src="/monitor/vms-modal-updates-04.png"
|
||||
alt={t("figures.f04.alt")}
|
||||
caption={t("figures.f04.caption")}
|
||||
/>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("figureOut.heading")}</h2>
|
||||
<p className="text-gray-800">{t("figureOut.intro")}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("figureOut.step1Heading")}</h3>
|
||||
<p className="text-gray-800">{t.rich("figureOut.step1P1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("figureOut.step1P2")}</p>
|
||||
<CopyableCode code={t("figureOut.step1Cmd1")} language="sh" />
|
||||
<p className="text-gray-800 mt-4">{t("figureOut.step1P3")}</p>
|
||||
<CopyableCode code={t("figureOut.step1Cmd2")} language="sh" />
|
||||
<p className="text-gray-800 mt-4">{t.rich("figureOut.step1P4", { strong, em, code })}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("figureOut.step2Heading")}</h3>
|
||||
<p className="text-gray-800">{t.rich("figureOut.step2P1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("figureOut.step2P2")}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("figureOut.step3Heading")}</h3>
|
||||
<p className="text-gray-800">{t("figureOut.step3Lead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{step3Items.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`figureOut.step3Items.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t("figureOut.step3P1")}</p>
|
||||
<CopyableCode code={t("figureOut.step3Cmd")} language="sh" />
|
||||
<p className="text-gray-800 mt-4">{t.rich("figureOut.step3P2", { strong, em, code })}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("figureOut.step4Heading")}</h3>
|
||||
<p className="text-gray-800">{t("figureOut.step4Lead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{step4Items.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`figureOut.step4Items.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t("figureOut.step4Note")}</p>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("figureOut.step5Heading")}</h3>
|
||||
<p className="text-gray-800">{t("figureOut.step5P1")}</p>
|
||||
<CopyableCode code={t.raw("figureOut.step5Cmd1") as string} language="text" />
|
||||
<p className="text-gray-800 mt-4">{t.rich("figureOut.step5P2", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("figureOut.step5P3")}</p>
|
||||
<CopyableCode code={t("figureOut.step5Cmd2")} language="sh" />
|
||||
<p className="text-gray-800 mt-4">{t("figureOut.step5P4")}</p>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("requirements.heading")}</h2>
|
||||
<p className="text-gray-800">{t("requirements.lead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{reqItems.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`requirements.items.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t.rich("requirements.trailing", { strong, em, code })}</p>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("difference.heading")}</h2>
|
||||
<p className="text-gray-800">{t("difference.lead")}</p>
|
||||
<div className="overflow-x-auto my-4">
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("mechanisms.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("mechanisms.lead", { strong, em, code, helper: linkHelper })}</p>
|
||||
<div className="my-4 overflow-x-auto">
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("difference.table.colField")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("difference.table.colLocation")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("difference.table.colRole")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("mechanisms.colSource")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("mechanisms.colAction")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("mechanisms.colNotes")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{diffRows.map((row, idx) => (
|
||||
{v.mechanisms.rows.map((row, idx) => (
|
||||
<tr key={idx}>
|
||||
<td className="border border-gray-300 px-3 py-2 font-medium">{row.field}</td>
|
||||
<td className="border border-gray-300 px-3 py-2 text-xs">{row.location}</td>
|
||||
<td className="border border-gray-300 px-3 py-2 text-xs">{row.role}</td>
|
||||
<td className="border border-gray-300 px-3 py-2 font-medium">{row.source}</td>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.action}</td>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.notes}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p className="text-gray-800 mt-4">{t.rich("difference.trailing", { strong, em, code })}</p>
|
||||
<Callout variant="warning">{t.rich("mechanisms.callout", { strong, em, code })}</Callout>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("apply.heading")}</h2>
|
||||
<p className="text-gray-800">{t("apply.lead")}</p>
|
||||
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{applySteps.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`apply.steps.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ol>
|
||||
<p className="text-gray-800 mt-4">{t("apply.trailing1")}</p>
|
||||
<p className="text-gray-800 mt-4">{t("apply.systemLead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{applySystem.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`apply.systemItems.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t("apply.appLead")}</p>
|
||||
<ul className="list-disc pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{applyApp.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`apply.appItems.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-gray-800 mt-4">{t("apply.trailing2")}</p>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("docker.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("docker.lead", { strong, em, code })}</p>
|
||||
{richList("docker.items", v.docker.items)}
|
||||
<Callout variant="info">{t.rich("docker.callout", { strong, em, code })}</Callout>
|
||||
|
||||
<Figure
|
||||
src="/monitor/vms-modal-updates-05.png"
|
||||
alt={t("figures.f05.alt")}
|
||||
caption={t("figures.f05.caption")}
|
||||
/>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("actions.heading")}</h2>
|
||||
<p className="text-gray-800">{t("actions.lead")}</p>
|
||||
{richList("actions.items", v.actions.items)}
|
||||
<div className="my-4 overflow-x-auto">
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("actions.statusColState")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("actions.statusColAppearance")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("actions.statusColMeaning")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{v.actions.statusRows.map((row, idx) => (
|
||||
<tr key={idx}>
|
||||
<td className="border border-gray-300 px-3 py-2 font-medium">{row.state}</td>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.appearance}</td>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.meaning}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("backup.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("backup.p1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("backup.p2")}</p>
|
||||
<p className="text-gray-800 mt-4">{t("backup.p3")}</p>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("custom.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("custom.lead", { strong, em, code })}</p>
|
||||
{richList("custom.items", v.custom.items)}
|
||||
<p className="mt-4 text-gray-800">{t("custom.exampleLead")}</p>
|
||||
<CopyableCode code={t("custom.example")} language="sh" />
|
||||
<Callout variant="warning">{t.rich("custom.callout", { strong, em, code })}</Callout>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("restart.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("restart.p1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("restart.p2")}</p>
|
||||
<p className="text-gray-800 mt-4">{t("restart.p3")}</p>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("bulk.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("bulk.lead", { strong, em, code })}</p>
|
||||
{richList("bulk.items", v.bulk.items)}
|
||||
<Callout variant="info">{t.rich("bulk.callout", { strong, em, code })}</Callout>
|
||||
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("options.heading")}</h2>
|
||||
<p className="text-gray-800">{t("options.lead")}</p>
|
||||
{richList("options.items", v.options.items)}
|
||||
<Figure
|
||||
src="/monitor/vms-modal-updates-06.png"
|
||||
alt={t("figures.f06.alt")}
|
||||
caption={t("figures.f06.caption")}
|
||||
alt={t("figures.options.alt")}
|
||||
caption={t("figures.options.caption")}
|
||||
/>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("scheduled.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("scheduled.p1", { strong, em, code })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("scheduled.createLead")}</p>
|
||||
<ol className="list-decimal pl-6 mt-2 space-y-1 text-gray-800">
|
||||
{schedSteps.map((_, idx) => (
|
||||
<li key={idx}>{t.rich(`scheduled.createSteps.${idx}`, { strong, em, code })}</li>
|
||||
))}
|
||||
</ol>
|
||||
<p className="text-gray-800 mt-4">{t("scheduled.p2")}</p>
|
||||
<p className="text-gray-800 mt-4">{t("scheduled.p3")}</p>
|
||||
<Callout variant="tip">{t("scheduled.callout")}</Callout>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("scheduled.heading")}</h2>
|
||||
<p className="text-gray-800">{t.rich("scheduled.lead", { strong, em, code })}</p>
|
||||
{richList("scheduled.items", v.scheduled.items)}
|
||||
<Callout variant="tip">{t.rich("scheduled.callout", { strong, em, code })}</Callout>
|
||||
|
||||
<Figure
|
||||
src="/monitor/vms-modal-updates-07.png"
|
||||
alt={t("figures.f07.alt")}
|
||||
caption={t("figures.f07.caption")}
|
||||
/>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("completion.heading")}</h2>
|
||||
<p className="text-gray-800">{t("completion.lead")}</p>
|
||||
{richList("completion.items", v.completion.items)}
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("verify.heading")}</h2>
|
||||
<p className="text-gray-800">{t("verify.p1")}</p>
|
||||
<p className="text-gray-800 mt-4">{t.rich("verify.p2", { strong, em, code, link: linkApp })}</p>
|
||||
<p className="text-gray-800 mt-4">{t("verify.p3")}</p>
|
||||
|
||||
<h2 className="text-2xl font-semibold mt-10 mb-4 text-gray-900">{t("troubleshoot.heading")}</h2>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("troubleshoot.noButtonHeading")}</h3>
|
||||
<Callout variant="troubleshoot">{t.rich("troubleshoot.noButtonBody", { strong, em, code })}</Callout>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("troubleshoot.aptHeading")}</h3>
|
||||
<Callout variant="troubleshoot">{t.rich("troubleshoot.aptBody", { strong, em, code })}</Callout>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("troubleshoot.noUpdaterHeading")}</h3>
|
||||
<Callout variant="troubleshoot">{t.rich("troubleshoot.noUpdaterBody", { strong, em, code })}</Callout>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("troubleshoot.helperDetectedHeading")}</h3>
|
||||
<Callout variant="troubleshoot">{t.rich("troubleshoot.helperDetectedBody", { strong, em, code })}</Callout>
|
||||
|
||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-gray-900">{t("troubleshoot.customFailsHeading")}</h3>
|
||||
<Callout variant="troubleshoot">{t.rich("troubleshoot.customFailsBody", { strong, em, code })}</Callout>
|
||||
<h2 className="mt-10 mb-4 text-2xl font-semibold text-gray-900">{t("troubleshooting.heading")}</h2>
|
||||
<div className="my-4 overflow-x-auto">
|
||||
<table className="w-full border-collapse text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-100">
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("troubleshooting.colProblem")}</th>
|
||||
<th className="border border-gray-300 px-3 py-2 text-left">{t("troubleshooting.colResolution")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{v.troubleshooting.rows.map((row, idx) => (
|
||||
<tr key={idx}>
|
||||
<td className="border border-gray-300 px-3 py-2 font-medium">{row.problem}</td>
|
||||
<td className="border border-gray-300 px-3 py-2">{row.resolution}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user