mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-17 00:55:07 +00:00
Put the update script's useful options one click away
The screen that tells somebody how to update names one command and stops, which is right — the command is the procedure. But two of the options behind it are the ones an administrator wants at exactly that moment: whether it can take the backup for them, and whether they can just look without changing anything. Both were documented only in UPDATE.md and in --help, neither of which is open on the screen they are reading. They are behind an "Other options" link rather than printed, so the page in its resting state is unchanged and the one command stays the thing you see. Four lines, the two above plus the two for a run nobody is sitting in front of. It opens from the dashboard card and from the update dialog both, which means a dialog on top of a dialog in the second case. That is the right shape here: the reader asked for a footnote to what they are already reading, and Escape puts them back. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { UpdateOptionsDialog } from '@/components/update-options-dialog';
|
||||
import { useTranslation } from '@/hooks/use-translation';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
@@ -45,9 +46,10 @@ export function UpdateInstructions({
|
||||
|
||||
if (compact) {
|
||||
return (
|
||||
<p className="text-muted-foreground">
|
||||
{t('To update, run sudo ./update.sh in the install directory — it asks before it downloads or changes anything.')}
|
||||
</p>
|
||||
<div className="text-muted-foreground space-y-1">
|
||||
<p>{t('To update, run sudo ./update.sh in the install directory — it asks before it downloads or changes anything.')}</p>
|
||||
<UpdateOptionsDialog />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,6 +67,9 @@ export function UpdateInstructions({
|
||||
'It asks before checking for a release, before downloading one, and before touching your installation — and verifies the checksum of what it downloads. UPDATE.md has the full procedure.',
|
||||
)}
|
||||
</p>
|
||||
<div className="mt-1">
|
||||
<UpdateOptionsDialog />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||
import { useTranslation } from '@/hooks/use-translation';
|
||||
|
||||
/**
|
||||
* The handful of update.sh options worth knowing about, behind a link
|
||||
* rather than printed on the page.
|
||||
*
|
||||
* The screen that tells somebody how to update has one job: the one
|
||||
* command. Listing every flag beside it would bury that command under
|
||||
* options most people never need — but two of them are the ones an
|
||||
* administrator most wants at exactly that moment ("can it back up for
|
||||
* me?", "can I just look?"), and finding them meant opening UPDATE.md on
|
||||
* a machine they are probably not sitting at.
|
||||
*
|
||||
* So: nothing on screen until asked for, and then four lines. The script
|
||||
* documents the rest with --help, and UPDATE.md documents the procedure.
|
||||
*
|
||||
* It opens from inside the update dialog as well as from the dashboard
|
||||
* card. A dialog on top of a dialog is unusual, and correct here — the
|
||||
* reader asked for a footnote to what they are already reading, and
|
||||
* Escape returns them to it.
|
||||
*/
|
||||
export function UpdateOptionsDialog() {
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const options: { flag: string; description: string }[] = [
|
||||
{ flag: '--backup', description: t('Dump the database before touching anything, and stop if the dump fails.') },
|
||||
{ flag: '--check', description: t('Report which version is installed and which is available, and change nothing.') },
|
||||
{ flag: '--i-have-a-backup', description: t('Skip the question about backups, for a run you are not sitting in front of.') },
|
||||
{ flag: '--no-restart', description: t('Leave PHP and the worker alone, for when you restart them yourself.') },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<button type="button" onClick={() => setOpen(true)} className="text-muted-foreground text-xs underline hover:no-underline">
|
||||
{t('Other options')}
|
||||
</button>
|
||||
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogContent className="max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('Update options')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-3 text-sm">
|
||||
<p className="text-muted-foreground">
|
||||
{t('Run the script with no options and it asks about each of these as it goes. These are for the runs where you already know the answer.')}
|
||||
</p>
|
||||
|
||||
<dl className="divide-y">
|
||||
{options.map((option) => (
|
||||
<div key={option.flag} className="grid gap-1 py-2 sm:grid-cols-[10rem_1fr] sm:gap-3">
|
||||
<dt>
|
||||
<code className="bg-muted rounded px-1.5 py-0.5 text-xs">{option.flag}</code>
|
||||
</dt>
|
||||
<dd className="text-muted-foreground">{option.description}</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t('sudo ./update.sh --help lists every option, and UPDATE.md walks through the whole procedure, including doing it by hand.')}
|
||||
</p>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user