@mui/material#DialogActions TypeScript Examples
The following examples show how to use
@mui/material#DialogActions.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: UTXODetail.tsx From sapio-studio with Mozilla Public License 2.0 | 6 votes |
function ContinuationOption(props: { v: Continuation }) {
const [is_open, setOpen] = React.useState(false);
const name = props.v.path.substr(props.v.path.lastIndexOf('/') + 1);
const dispatch = useDispatch();
return (
<div>
<Button onClick={() => setOpen(true)} variant="contained">
{name}
</Button>
<Dialog open={is_open} onClose={() => setOpen(false)}>
<DialogTitle>
<Typography variant="h5">{name}</Typography>
<ASM
className="txhex"
value={props.v.path}
label="Full Path"
/>
</DialogTitle>
<DialogContent>
<MemoizeContForm {...props} />
</DialogContent>
<DialogActions>
<Button onClick={() => dispatch(recreate_contract())}>
Recompile
</Button>
<Button onClick={() => setOpen(false)}>Close</Button>
</DialogActions>
</Dialog>
</div>
);
}
Example #2
Source File: Home.tsx From mui-toolpad with MIT License | 6 votes |
function AppDeleteDialog({ app, onClose }: AppDeleteDialogProps) {
const latestApp = useLatest(app);
const deleteAppMutation = client.useMutation('deleteApp');
const handleDeleteClick = React.useCallback(async () => {
if (app) {
await deleteAppMutation.mutateAsync([app.id]);
}
await client.refetchQueries('getApps');
onClose();
}, [app, deleteAppMutation, onClose]);
return (
<Dialog open={!!app} onClose={onClose}>
<DialogForm>
<DialogTitle>Confirm delete</DialogTitle>
<DialogContent>
Are you sure you want to delete application "{latestApp?.name}"
</DialogContent>
<DialogActions>
<Button color="inherit" variant="text" onClick={onClose}>
Cancel
</Button>
<LoadingButton
type="submit"
loading={deleteAppMutation.isLoading}
onClick={handleDeleteClick}
color="error"
>
Delete
</LoadingButton>
</DialogActions>
</DialogForm>
</Dialog>
);
}
Example #3
Source File: Files.tsx From NekoMaid with MIT License | 6 votes |
CompressDialog: React.FC<{ file: string | null, dirs: Record<string, boolean>, onClose: () => void, plugin: Plugin, path: string, refresh: () => void }> =
({ dirs, file, onClose, plugin, path, refresh }) => {
const [value, setValue] = useState('')
const [ext, setExt] = useState('zip')
useEffect(() => {
setValue(file || 'server')
}, [file])
let error: string | undefined
if (!validFilename(value)) error = lang.files.wrongName
else if (((path || '/') + value + '.' + ext) in dirs) error = lang.files.exists
return <Dialog open={file != null} onClose={onClose}>
<DialogTitle>{lang.files.compress}</DialogTitle>
<DialogContent>
<DialogContentText>{lang.files.compressName}</DialogContentText>
<TextField value={value} variant='standard' error={!!error} helperText={error} onChange={e => setValue(e.target.value)} />
<Select variant='standard' value={ext} onChange={e => setExt(e.target.value)}>
{compressFileExt.map(it => <MenuItem key={it} value={it}>.{it}</MenuItem>)}
</Select>
</DialogContent>
<DialogActions>
<Button onClick={onClose}>{minecraft['gui.cancel']}</Button>
<Button disabled={!!error} onClick={() => {
onClose()
plugin.emit('files:compress', (res: boolean) => {
action(res)
refresh()
}, file, value, ext)
}}>{minecraft['gui.ok']}</Button>
</DialogActions>
</Dialog>
}
Example #4
Source File: DeleteSelectedModal.tsx From frontend with MIT License | 6 votes |
function DeleteSelectedModal({
isOpen,
handleDeleteModalClose,
handleDelete,
}: {
isOpen: boolean
handleDeleteModalClose: () => void
handleDelete: () => void
}) {
return (
<Modal open={isOpen} onClose={handleDeleteModalClose}>
<Dialog open={isOpen} onClose={handleDeleteModalClose}>
<DialogTitle>Искате ли да изтриете избраните кампании?</DialogTitle>
<DialogActions>
<Button onClick={handleDelete} autoFocus>
Да
</Button>
<Button onClick={handleDeleteModalClose}>Не</Button>
</DialogActions>
</Dialog>
</Modal>
)
}
Example #5
Source File: GroupDeleteModal.tsx From abrechnung with GNU Affero General Public License v3.0 | 6 votes |
export default function GroupDeleteModal({ show, onClose, groupToDelete }) {
const confirmDeleteGroup = () => {
deleteGroup({ groupID: groupToDelete.id })
.then((res) => {
onClose();
})
.catch((err) => {
toast.error(err);
});
};
return (
<Dialog open={show} onClose={onClose}>
<DialogTitle>Delete Group</DialogTitle>
<DialogContent>
<DialogContentText>
{groupToDelete ? <span>Are you sure you want to delete group {groupToDelete.name}</span> : null}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="primary" onClick={onClose}>
No
</Button>
<Button color="error" onClick={confirmDeleteGroup}>
Yes pls
</Button>
</DialogActions>
</Dialog>
);
}
Example #6
Source File: FaceTimeLinkDialog.tsx From airmessage-web with Apache License 2.0 | 6 votes |
export default function FaceTimeLinkDialog(props: {
isOpen: boolean,
onDismiss: () => void,
link: string
}) {
const propsLink = props.link;
const propsOnDismiss = props.onDismiss;
const copyLink = useCallback(async () => {
await navigator.clipboard.writeText(propsLink);
propsOnDismiss();
}, [propsLink, propsOnDismiss]);
return (
<Dialog
open={props.isOpen}
onClose={props.onDismiss}>
<DialogTitle>FaceTime link</DialogTitle>
<DialogContent>
<DialogContentText>
{props.link}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={props.onDismiss} color="primary">
Cancel
</Button>
<Button onClick={copyLink} color="primary" autoFocus>
Copy
</Button>
</DialogActions>
</Dialog>
);
}
Example #7
Source File: cookies-dialog.tsx From master-frontend-lemoncode with MIT License | 6 votes |
CookiesDialog: React.FunctionComponent<Props> = (props) => {
const { onAgreeClick } = props;
const [open, setOpen] = React.useState(false);
const handleAgreeClick = () => {
setOpen(false);
onAgreeClick();
};
return (
<>
<Button variant="outlined" onClick={() => setOpen(true)}>
Learn more about our cookies
</Button>
<Dialog open={open} onClose={() => setOpen(false)}>
<DialogTitle>About cookies</DialogTitle>
<DialogContent>
<DialogContentText>
Any information that you voluntarily provide to us, including your
name and email address, will be used for the sole purpose for which
the information was provided to us. In addition, communication
exchanges on this website are public (not private) communications.
Therefore, any message that you post on this website will be
considered and treated as available for public use and distribution.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="primary" onClick={handleAgreeClick}>
Agree
</Button>
</DialogActions>
</Dialog>
</>
);
}
Example #8
Source File: ServerSwitch.tsx From NekoMaid with MIT License | 5 votes |
ServerSwitch: React.FC<DialogProps> = props => {
const [value, setValue] = useState<string>('')
let error = false
// eslint-disable-next-line no-new
try { if (value) new URL(value.startsWith('http://') ? value : 'http://' + value) } catch { error = true }
return <Dialog fullWidth maxWidth='xs' {...props}>
<DialogTitle>{lang.serverSwitch.title}</DialogTitle>
<DialogContent sx={{ overflow: 'hidden' }}>
<Autocomplete
freeSolo
inputValue={value}
clearOnBlur={false}
onInputChange={(_: any, v: string) => setValue(v)}
noOptionsText={lang.serverSwitch.noServer}
style={{ width: '100%', maxWidth: 500, marginTop: 10 }}
options={JSON.parse(localStorage.getItem('NekoMaid:servers') || '[]')}
getOptionLabel={(option: any) => option.address}
renderInput={(props: any) => <TextField
{...props}
error={error}
label={minecraft['addServer.enterIp']}
helperText={error ? lang.serverSwitch.wrongHostname : undefined}
/>}
/>
<DialogContentText>{lang.serverSwitch.content}</DialogContentText>
</DialogContent>
<DialogActions>
<Button
color='primary'
disabled={error}
onClick={() => (location.search = '?' + encodeURIComponent(value))}
>{lang.serverSwitch.connect}</Button>
</DialogActions>
</Dialog>
}
Example #9
Source File: FeedbackDialog.tsx From airmessage-web with Apache License 2.0 | 5 votes |
/**
* A dialog that presents help and feedback options
*/
export default function FeedbackDialog(props: {isOpen: boolean, onDismiss: () => void}) {
const propsOnDismiss = props.onDismiss;
const onClickEmail = useCallback(async () => {
const body =
`\n\n---------- DEVICE INFORMATION ----------` +
Object.entries(await getPlatformUtils().getExtraEmailDetails())
.map(([key, value]) => `\n${key}: ${value}`)
.join("") +
`\nUser agent: ${navigator.userAgent}` +
`\nClient version: ${appVersion}` +
`\nCommunications version: ${getActiveCommVer()?.join(".")} (target ${targetCommVerString})` +
`\nProxy type: ${getActiveProxyType()}` +
`\nServer system version: ${getServerSystemVersion()}` +
`\nServer software version: ${getServerSoftwareVersion()}`;
const url = `mailto:${supportEmail}?subject=${encodeURIComponent("AirMessage feedback")}&body=${encodeURIComponent(body)}`;
window.open(url, "_blank");
propsOnDismiss();
}, [propsOnDismiss]);
const onClickCommunity = useCallback(() => {
window.open(communityPage, "_blank");
propsOnDismiss();
}, [propsOnDismiss]);
return (
<Dialog
open={props.isOpen}
onClose={props.onDismiss}>
<DialogTitle>Help and feedback</DialogTitle>
<DialogContent>
<DialogContentText>
Have a bug to report, a feature to suggest, or anything else to say? Contact us or discuss with others using the links below.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={onClickEmail} color="primary">
Send E-Mail
</Button>
<Button onClick={onClickCommunity} color="primary" autoFocus>
Open community subreddit
</Button>
</DialogActions>
</Dialog>
);
}
Example #10
Source File: QueryStateEditor.tsx From mui-toolpad with MIT License | 5 votes |
export default function QueryStateEditor() {
const dom = useDom();
const state = usePageEditorState();
const domApi = useDomApi();
const [editedState, setEditedState] = React.useState<NodeId | null>(null);
const editedStateNode = editedState ? appDom.getNode(dom, editedState, 'queryState') : null;
const handleEditStateDialogClose = React.useCallback(() => setEditedState(null), []);
const page = appDom.getNode(dom, state.nodeId, 'page');
const { queryStates = [] } = appDom.getChildNodes(dom, page) ?? [];
// To keep dialog content around during closing animation
const lastEditedStateNode = useLatest(editedStateNode);
const handleRemove = React.useCallback(() => {
if (editedStateNode) {
domApi.removeNode(editedStateNode.id);
}
handleEditStateDialogClose();
}, [editedStateNode, handleEditStateDialogClose, domApi]);
return queryStates.length > 0 ? (
<Stack spacing={1} alignItems="start">
<List>
{queryStates.map((stateNode) => {
return (
<ListItem key={stateNode.id} button onClick={() => setEditedState(stateNode.id)}>
{stateNode.name}
</ListItem>
);
})}
</List>
{lastEditedStateNode ? (
<Dialog
fullWidth
maxWidth="lg"
open={!!editedStateNode}
onClose={handleEditStateDialogClose}
scroll="body"
>
<DialogTitle>Edit Query State ({lastEditedStateNode.id})</DialogTitle>
<DialogContent>
<QueryStateNodeEditor node={lastEditedStateNode} />
</DialogContent>
<DialogActions>
<Button color="inherit" variant="text" onClick={handleEditStateDialogClose}>
Close
</Button>
<Button onClick={handleRemove}>Remove</Button>
</DialogActions>
</Dialog>
) : null}
</Stack>
) : null;
}
Example #11
Source File: ConfirmationDialog.tsx From console with GNU Affero General Public License v3.0 | 5 votes |
ConfirmationDialog = ({
classes,
open,
cancelLabel,
okLabel,
onClose,
cancelOnClick,
okOnClick,
title,
description,
}: IConfirmationDialog) => {
const [isSending, setIsSending] = useState<boolean>(false);
const onClick = () => {
setIsSending(true);
if (okOnClick !== null) {
okOnClick();
}
setIsSending(false);
};
if (!open) return null;
return (
<Dialog
open={open}
onClose={onClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">{title}</DialogTitle>
<DialogContent>
{isSending && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
{description}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={cancelOnClick} color="primary" disabled={isSending}>
{cancelLabel || "Cancel"}
</Button>
<Button onClick={onClick} color="secondary" autoFocus>
{okLabel || "Ok"}
</Button>
</DialogActions>
</Dialog>
);
}
Example #12
Source File: CreatePageNodeDialog.tsx From mui-toolpad with MIT License | 5 votes |
export default function CreatePageDialog({ appId, onClose, ...props }: CreatePageDialogProps) {
const dom = useDom();
const domApi = useDomApi();
const [name, setName] = React.useState('');
const navigate = useNavigate();
return (
<Dialog {...props} onClose={onClose}>
<DialogForm
autoComplete="off"
onSubmit={(e) => {
e.preventDefault();
const newNode = appDom.createNode(dom, 'page', {
name,
attributes: {
title: appDom.createConst(name),
urlQuery: appDom.createConst({}),
},
});
const appNode = appDom.getApp(dom);
domApi.addNode(newNode, appNode, 'pages');
onClose();
navigate(`/app/${appId}/editor/pages/${newNode.id}`);
}}
>
<DialogTitle>Create a new MUI Toolpad Page</DialogTitle>
<DialogContent>
<TextField
sx={{ my: 1 }}
autoFocus
fullWidth
label="name"
value={name}
onChange={(event) => setName(event.target.value)}
/>
</DialogContent>
<DialogActions>
<Button color="inherit" variant="text" onClick={onClose}>
Cancel
</Button>
<Button type="submit" disabled={!name}>
Create
</Button>
</DialogActions>
</DialogForm>
</Dialog>
);
}
Example #13
Source File: WalletSendDialog.tsx From sapio-studio with Mozilla Public License 2.0 | 5 votes |
export function WalletSendDialog(props: {
show: boolean;
amt: number;
to: string;
close: () => void;
bitcoin_node_manager: BitcoinNodeManager;
}) {
return (
<Dialog
open={props.show}
onClose={() => {
props.close();
}}
>
<DialogTitle>Confirm Spend</DialogTitle>
<DialogContent>
<DialogContentText>
Confirm sending
{props.amt} BTC to {props.to}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
props.close();
}}
>
Cancel
</Button>
<Button
onClick={async () => {
await props.bitcoin_node_manager.send_to_address(
props.amt,
props.to
);
props.close();
}}
>
Confirm
</Button>
</DialogActions>
</Dialog>
);
}
Example #14
Source File: StatsDialog.tsx From GTAV-NativeDB with MIT License | 5 votes |
export default function StatsDialog({ open, onClose }: Props) {
const stats = useStats()
return (
<Dialog open={open} onClose={onClose} fullWidth maxWidth="xs">
<DialogTitle>
Stats
</DialogTitle>
<List dense>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Namespaces"
secondary={stats.namespaces}
/>
</ListItem>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Natives"
secondary={stats.natives}
/>
</ListItem>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Comments"
secondary={stats.comments}
/>
</ListItem>
<ListItem sx={{ px: 3 }} >
<ListItemText
primary="Known names"
secondary={`${stats.knownNames.confirmed} (${stats.knownNames.total})`}
/>
</ListItem>
</List>
<DialogActions>
<Button onClick={onClose}>Close</Button>
</DialogActions>
</Dialog>
)
}
Example #15
Source File: dialog.tsx From NekoMaid with MIT License | 5 votes |
DialogWrapper: React.FC = () => {
const [canClick, setCanClick] = useState(false)
const [open, setOpen] = useState(false)
const [text, setText] = useState('')
const [data, setDate] = useState<DialogOptionsWithPromise | undefined>()
useEffect(() => {
openFn = it => {
setDate(it)
setOpen(true)
}
}, [])
if (!data) return <></>
const input = (data as any).input
const cancel = () => {
setOpen(false)
setDate(undefined)
setText('')
data.resolve(input ? null : false)
}
let inputElm: React.ReactNode
if (input) {
const props: any = {
key: input.label || input,
autoFocus: true,
fullWidth: true,
margin: 'dense',
variant: 'standard',
value: text,
onStatusChange: setCanClick,
onChange (it: any) { setText(it.target.value) }
}
if (typeof input === 'string') props.label = input
else if (typeof input === 'object') Object.assign(props, input)
inputElm = React.createElement(ValidInput, props)
}
return <Dialog open={!!open} onClose={cancel}>
<DialogTitle>{data.title || lang.tip}</DialogTitle>
<DialogContent>
<DialogContentText>{data.content}</DialogContentText>
{inputElm}
</DialogContent>
<DialogActions>
{data.cancelButton !== false && <Button onClick={cancel}>{minecraft['gui.cancel']}</Button>}
<Button {...data.okButton} disabled={canClick} onClick={() => {
setOpen(false)
setDate(undefined)
setText('')
data.resolve((data as any).input ? text : true)
}}>{minecraft['gui.ok']}</Button>
</DialogActions>
</Dialog>
}
Example #16
Source File: DeleteDialog.tsx From sapio-studio with Mozilla Public License 2.0 | 5 votes |
export function DeleteDialog(props: { set_to_delete: () => void; to_delete: ['workspace', string] | ['contract', string] | null; reload: () => void; }) { const workspace = useSelector(selectWorkspace); return ( <Dialog onClose={props.set_to_delete} open={props.to_delete !== null}> <DialogTitle>Confirm Deletion</DialogTitle> <DialogContent> <DialogContentText> Confirm deletion of "{props.to_delete}"? File will be in your trash folder. </DialogContentText> </DialogContent> <DialogActions> <Button color="warning" onClick={(ev) => { if (props.to_delete) { switch (props.to_delete[0]) { case 'workspace': window.electron.sapio.workspaces.trash( props.to_delete[1] ); break; case 'contract': window.electron.sapio.compiled_contracts.trash( workspace, props.to_delete[1] ); break; } } props.set_to_delete(); props.reload(); }} > Delete </Button> <Button onClick={props.set_to_delete}>Cancel</Button> </DialogActions> </Dialog> ); }
Example #17
Source File: PersonSelectDialog.tsx From frontend with MIT License | 5 votes |
function PersonSelectDialog({ onConfirm: confirmCallback, onClose: closeCallback, error }: Props) {
const [person, setPerson] = useState<PersonResponse | null>(null)
const { t } = useTranslation()
const { open, confirmHandler, closeHandler, openHandler, loading } = useConfirm({
onConfirm: async () => {
confirmCallback ? confirmCallback(person) : null
},
onClose: async () => {
closeCallback ? closeCallback(person) : null
},
})
return (
<>
<FormFieldButton
onClick={openHandler}
placeholder={t('person:selectDialog.notSelected')}
value={person ? `${person.firstName} ${person.lastName} (${person.id})` : undefined}
button={{ label: t('person:selectDialog.select') }}
error={error ? translateError(error, t) : undefined}
/>
<Dialog fullWidth open={open} onClose={closeHandler}>
<DialogTitle>{t('person:selectDialog.personSelect')}</DialogTitle>
<DialogContent>
<Box sx={{ marginTop: theme.spacing(2) }}>
<PersonAutocomplete
onSelect={(person) => {
setPerson(person)
}}
showId
autocompleteProps={{ defaultValue: person }}
/>
</Box>
<Box sx={{ marginTop: theme.spacing(2) }}>
{person ? <PersonInfo person={person} /> : t('person:selectDialog.notSelected')}
</Box>
</DialogContent>
<DialogActions>
<CloseModalButton onClose={closeHandler} />
<LoadingButton onClick={confirmHandler} loading={loading}>
{t('person:selectDialog.confirm')}
</LoadingButton>
</DialogActions>
</Dialog>
</>
)
}
Example #18
Source File: NewNickname.tsx From sapio-studio with Mozilla Public License 2.0 | 5 votes |
export function NewNickname(props: { show: boolean; hide: () => void }) {
const [value, set_value] = React.useState<null | string>(null);
const [key_value, set_key_value] = React.useState<null | string>(null);
return (
<Dialog onClose={props.hide} open={props.show}>
<DialogTitle>Create a new User</DialogTitle>
<DialogContent>
<DialogContentText>
The key and nickname for the new person. Keys must be
unique.
</DialogContentText>
<TextField
onChange={(ev) => set_value(ev.currentTarget.value)}
value={value}
autoFocus
margin="dense"
label="Name"
name="name"
type="text"
fullWidth
variant="standard"
/>
<TextField
onChange={(ev) => set_key_value(ev.currentTarget.value)}
value={key_value}
autoFocus
margin="dense"
label="Key Hash"
name="keyhash"
type="text"
fullWidth
variant="standard"
/>
</DialogContent>
<DialogActions>
<Button onClick={props.hide}>Cancel</Button>
<Button
color="success"
onClick={async (ev) => {
if (value !== null && key_value) {
await window.electron.chat.add_user(
value,
key_value
);
}
props.hide();
}}
>
Create
</Button>
</DialogActions>
</Dialog>
);
}
Example #19
Source File: PurchaseDialogueBody.tsx From mojito_pdm with Creative Commons Attribution Share Alike 4.0 International | 5 votes |
PurchaseDialogueBody: React.FC<IPurchaseDialogueBody> = ({spawncode, price, setDialogueOpen, setModalOpen}) => {
const [colour, setColour] = useState<RgbColor>({r: 0, g: 0, b: 0})
const handleClose = () => {
setDialogueOpen(false)
}
const {setVisible} = useVisibility()
const coloursEnabled = useRecoilValue(GlobalState.customColours)
const handleAccept = async () => {
setDialogueOpen(false)
setModalOpen(false)
try {
await fetchNui("buy_vehicle", {
vehicle: spawncode,
colour: coloursEnabled ? colour : null
})
await fetchNui("exit")
setVisible(false)
} catch (e) {
console.error(e)
}
}
return (
<>
<DialogTitle>Are you sure?</DialogTitle>
<DialogContent>
<DialogContentText>
Do you want to purchase this vehicle for {price}?
</DialogContentText>
{coloursEnabled &&
<DialogContentText>
<br />
Pick a colour, any colour:
<RgbColorPicker color={colour} onChange={setColour} />
</DialogContentText>
}
</DialogContent>
<DialogActions>
<Button color="success" variant="outlined" onClick={handleAccept}>Yes</Button>
<Button color="error" variant="outlined" onClick={handleClose}>Cancel</Button>
</DialogActions>
</>
)
}
Example #20
Source File: Settings.tsx From sapio-studio with Mozilla Public License 2.0 | 4 votes |
export function SettingsInner() {
const [idx, set_idx] = React.useState<number>(0);
const [dialog_node, set_dialog_node] = React.useState<
[string | null, string[]]
>([null, []]);
const handleChange = (_: any, idx: number) => {
set_idx(idx);
};
const test_bitcoind = async () => {
window.electron
.bitcoin_command([{ method: 'getbestblockhash', parameters: [] }])
.then((h) =>
set_dialog_node(['Connection Seems OK:', [`Best Hash ${h[0]}`]])
)
.catch((e) => {
console.log('GOT', JSON.stringify(e));
const r = e.message;
if (typeof e.message === 'string') {
const err = JSON.parse(r);
if (
err instanceof Object &&
'code' in err &&
'name' in err &&
'message' in err
) {
set_dialog_node([
'¡Connection Not Working!',
[
`Name: ${err.name}`,
`Message: ${err.message}`,
`Error Code: ${err.code}`,
],
]);
return;
} else if (typeof err === 'string') {
set_dialog_node([
'¡Connection Not Working!',
[`${err}`],
]);
return;
}
}
set_dialog_node(['¡Unknown Error!', [`${r.toString()}`]]);
});
};
const test_sapio = async () => {
window.electron.sapio
.show_config()
.then((conf) => {
if ('ok' in conf)
set_dialog_node([
'Sapio-CLI is Working!\nUsing Configuration:',
[`${conf.ok}`],
]);
else
set_dialog_node(['¡Configuration Error!', [`${conf.err}`]]);
})
.catch((e) =>
set_dialog_node(['¡Configuration Error!', [`${e.toString()}`]])
);
};
const check_emulator = async () => {
window.electron.emulator.read_log().then((log) => {
if (log.length) {
const json = JSON.parse(log);
set_dialog_node([
'Emulator Status:',
[
`interface: ${json.interface}`,
`pk: ${json.pk}`,
`sync: ${json.sync}`,
],
]);
} else {
set_dialog_node(['Emulator Status:', ['Not Running']]);
}
});
};
return (
<div className="Settings">
<Box className="SettingsNav">
<Tabs
orientation="vertical"
value={idx}
onChange={handleChange}
aria-label="basic tabs example"
>
<Tab label="Guide"></Tab>
<Tab label="Sapio CLI"></Tab>
<Tab label="Bitcoin"></Tab>
<Tab label="Emulator"></Tab>
<Tab label="Display"></Tab>
</Tabs>
</Box>
<Box className="SettingsPanes">
<Dialog
open={
Boolean(dialog_node[0]) ||
Boolean(dialog_node[1].length)
}
onClose={() => set_dialog_node([null, []])}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{dialog_node[0]}
</DialogTitle>
<DialogContent>
<div id="alert-dialog-description">
{dialog_node[1].map((txt) => (
<DialogContentText key={txt}>
{txt}
</DialogContentText>
))}
</div>
</DialogContent>
<DialogActions>
<Button onClick={() => set_dialog_node([null, []])}>
Close
</Button>
</DialogActions>
</Dialog>
<Guide idx={idx} my_idx={0} />
<SettingPane name={'sapio_cli'} value={idx} idx={1}>
<Button
onClick={test_sapio}
variant="contained"
color="info"
size="large"
>
Test Sapio-Cli
</Button>
</SettingPane>
<SettingPane name={'bitcoin'} value={idx} idx={2}>
<Button
onClick={test_bitcoind}
variant="contained"
color="info"
size="large"
>
Test Connection
</Button>
</SettingPane>
<SettingPane name={'local_oracle'} value={idx} idx={3}>
<Button
variant="contained"
color="success"
size="large"
onClick={window.electron.emulator.start}
>
Start
</Button>
<Button
sx={{ marginLeft: '20px' }}
variant="contained"
color="error"
size="large"
onClick={window.electron.emulator.kill}
>
Kill
</Button>
<Button
sx={{ marginLeft: '20px' }}
variant="contained"
color="info"
size="large"
onClick={check_emulator}
>
Check Status
</Button>
</SettingPane>
<SettingPane name={'display'} value={idx} idx={4} />
</Box>
</div>
);
}
Example #21
Source File: index.tsx From mui-toolpad with MIT License | 4 votes |
export default function HierarchyExplorer({ appId, className }: HierarchyExplorerProps) {
const dom = useDom();
const domApi = useDomApi();
const app = appDom.getApp(dom);
const {
apis = [],
codeComponents = [],
pages = [],
connections = [],
} = appDom.getChildNodes(dom, app);
const [expanded, setExpanded] = useLocalStorageState<string[]>(
`editor/${app.id}/hierarchy-expansion`,
[':connections', ':pages', ':codeComponents'],
);
const location = useLocation();
const match =
matchRoutes(
[
{ path: `/app/:appId/editor/pages/:activeNodeId` },
{ path: `/app/:appId/editor/apis/:activeNodeId` },
{ path: `/app/:appId/editor/codeComponents/:activeNodeId` },
{ path: `/app/:appId/editor/connections/:activeNodeId` },
],
location,
) || [];
const selected: NodeId[] = match.map((route) => route.params.activeNodeId as NodeId);
const handleToggle = (event: React.SyntheticEvent, nodeIds: string[]) => {
setExpanded(nodeIds as NodeId[]);
};
const navigate = useNavigate();
const handleSelect = (event: React.SyntheticEvent, nodeIds: string[]) => {
if (nodeIds.length <= 0) {
return;
}
const rawNodeId = nodeIds[0];
if (rawNodeId.startsWith(':')) {
return;
}
const selectedNodeId: NodeId = rawNodeId as NodeId;
const node = appDom.getNode(dom, selectedNodeId);
if (appDom.isElement(node)) {
// TODO: sort out in-page selection
const page = appDom.getPageAncestor(dom, node);
if (page) {
navigate(`/app/${appId}/editor/pages/${page.id}`);
}
}
if (appDom.isPage(node)) {
navigate(`/app/${appId}/editor/pages/${node.id}`);
}
if (appDom.isApi(node)) {
navigate(`/app/${appId}/editor/apis/${node.id}`);
}
if (appDom.isCodeComponent(node)) {
navigate(`/app/${appId}/editor/codeComponents/${node.id}`);
}
if (appDom.isConnection(node)) {
navigate(`/app/${appId}/editor/connections/${node.id}`);
}
};
const [createConnectionDialogOpen, setCreateConnectionDialogOpen] = React.useState(0);
const handleCreateConnectionDialogOpen = React.useCallback((event: React.MouseEvent) => {
event.stopPropagation();
setCreateConnectionDialogOpen(Math.random());
}, []);
const handleCreateConnectionDialogClose = React.useCallback(
() => setCreateConnectionDialogOpen(0),
[],
);
const [createPageDialogOpen, setCreatePageDialogOpen] = React.useState(0);
const handleCreatePageDialogOpen = React.useCallback((event: React.MouseEvent) => {
event.stopPropagation();
setCreatePageDialogOpen(Math.random());
}, []);
const handleCreatepageDialogClose = React.useCallback(() => setCreatePageDialogOpen(0), []);
const [createCodeComponentDialogOpen, setCreateCodeComponentDialogOpen] = React.useState(0);
const handleCreateCodeComponentDialogOpen = React.useCallback((event: React.MouseEvent) => {
event.stopPropagation();
setCreateCodeComponentDialogOpen(Math.random());
}, []);
const handleCreateCodeComponentDialogClose = React.useCallback(
() => setCreateCodeComponentDialogOpen(0),
[],
);
const [deletedNodeId, setDeletedNodeId] = React.useState<NodeId | null>(null);
const handleDeleteNodeDialogOpen = React.useCallback(
(nodeId: NodeId) => (event: React.MouseEvent) => {
event.stopPropagation();
setDeletedNodeId(nodeId);
},
[],
);
const handledeleteNodeDialogClose = React.useCallback(() => setDeletedNodeId(null), []);
const handleDeleteNode = React.useCallback(() => {
if (deletedNodeId) {
domApi.removeNode(deletedNodeId);
navigate(`/app/${appId}/editor/`);
handledeleteNodeDialogClose();
}
}, [deletedNodeId, domApi, navigate, appId, handledeleteNodeDialogClose]);
const deletedNode = deletedNodeId && appDom.getMaybeNode(dom, deletedNodeId);
const latestDeletedNode = useLatest(deletedNode);
return (
<HierarchyExplorerRoot className={className}>
<TreeView
aria-label="hierarchy explorer"
selected={selected}
onNodeSelect={handleSelect}
expanded={expanded}
onNodeToggle={handleToggle}
multiSelect
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
>
<HierarchyTreeItem
nodeId=":connections"
labelText="Connections"
onCreate={handleCreateConnectionDialogOpen}
>
{connections.map((connectionNode) => (
<HierarchyTreeItem
key={connectionNode.id}
nodeId={connectionNode.id}
labelText={connectionNode.name}
onDelete={handleDeleteNodeDialogOpen(connectionNode.id)}
/>
))}
</HierarchyTreeItem>
{apis.length > 0 ? (
<HierarchyTreeItem nodeId=":apis" labelText="Apis">
{apis.map((apiNode) => (
<HierarchyTreeItem
key={apiNode.id}
nodeId={apiNode.id}
labelText={apiNode.name}
onDelete={handleDeleteNodeDialogOpen(apiNode.id)}
/>
))}
</HierarchyTreeItem>
) : null}
<HierarchyTreeItem
nodeId=":codeComponents"
labelText="Components"
onCreate={handleCreateCodeComponentDialogOpen}
>
{codeComponents.map((codeComponent) => (
<HierarchyTreeItem
key={codeComponent.id}
nodeId={codeComponent.id}
labelText={codeComponent.name}
onDelete={handleDeleteNodeDialogOpen(codeComponent.id)}
/>
))}
</HierarchyTreeItem>
<HierarchyTreeItem nodeId=":pages" labelText="Pages" onCreate={handleCreatePageDialogOpen}>
{pages.map((page) => (
<HierarchyTreeItem
key={page.id}
nodeId={page.id}
labelText={page.name}
onDelete={handleDeleteNodeDialogOpen(page.id)}
/>
))}
</HierarchyTreeItem>
</TreeView>
<CreateConnectionNodeDialog
key={createConnectionDialogOpen || undefined}
appId={appId}
open={!!createConnectionDialogOpen}
onClose={handleCreateConnectionDialogClose}
/>
<CreatePageNodeDialog
key={createPageDialogOpen || undefined}
appId={appId}
open={!!createPageDialogOpen}
onClose={handleCreatepageDialogClose}
/>
<CreateCodeComponentNodeDialog
key={createCodeComponentDialogOpen || undefined}
appId={appId}
open={!!createCodeComponentDialogOpen}
onClose={handleCreateCodeComponentDialogClose}
/>
<Dialog open={!!deletedNode} onClose={handledeleteNodeDialogClose}>
<DialogTitle>
Delete {latestDeletedNode?.type} "{latestDeletedNode?.name}"?
</DialogTitle>
<DialogActions>
<Button
type="submit"
color="inherit"
variant="text"
onClick={handledeleteNodeDialogClose}
>
Cancel
</Button>
<Button type="submit" onClick={handleDeleteNode}>
Delete
</Button>
</DialogActions>
</Dialog>
</HierarchyExplorerRoot>
);
}
Example #22
Source File: ItemViewer.tsx From NekoMaid with MIT License | 4 votes |
ItemEditor: React.FC = () => {
const plugin = usePlugin()
const theme = useTheme()
const [item, setItem] = useState<Item | undefined>()
const [types, setTypes] = useState<string[]>([])
const [tab, setTab] = useState(0)
const [level, setLevel] = useState(1)
const [enchantment, setEnchantment] = useState<string | undefined>()
const [nbtText, setNBTText] = useState('')
const nbt: NBT = item?.nbt ? parse(item.nbt) : { id: 'minecraft:' + (item?.type || 'air').toLowerCase(), Count: new Byte(1) } as any
useEffect(() => {
if (!item || types.length) return
plugin.emit('item:fetch', (a: string[], b: string[]) => {
setTypes(a)
enchantments = b
})
}, [item])
useEffect(() => {
_setItem = (it: any) => {
setItem(it)
setNBTText(it.nbt ? stringify(parse(it.nbt), { pretty: true }) : '')
}
return () => { _setItem = null }
}, [])
const cancel = () => {
setItem(undefined)
if (_resolve) {
_resolve(false)
_resolve = null
}
}
const update = () => {
const newItem: any = { ...item }
if (nbt) {
newItem.nbt = stringify(nbt as any)
setNBTText(stringify(nbt, { pretty: true }))
}
setItem(newItem)
}
const isAir = item?.type === 'AIR'
const name = nbt?.tag?.display?.Name
const enchantmentMap: Record<string, true> = { }
return <Dialog open={!!item} onClose={cancel}>
<DialogTitle>{lang.itemEditor.title}</DialogTitle>
<DialogContent sx={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center' }}>
{item && <Box sx={{ display: 'flex', width: '100%', justifyContent: 'center' }}>
<ItemViewer item={item} />
<Autocomplete
options={types}
sx={{ maxWidth: 300, marginLeft: 1, flexGrow: 1 }}
value={item?.type}
onChange={(_, it) => {
item.type = it || 'AIR'
if (nbt) nbt.id = 'minecraft:' + (it ? it.toLowerCase() : 'air')
update()
}}
getOptionLabel={it => {
const locatedName = getName(it.toLowerCase())
return (locatedName ? locatedName + ' ' : '') + it
}}
renderInput={(params) => <TextField {...params} label={lang.itemEditor.itemType} size='small' variant='standard' />}
/>
</Box>}
<Tabs centered value={tab} onChange={(_, it) => setTab(it)} sx={{ marginBottom: 2 }}>
<Tab label={lang.itemEditor.baseAttribute} disabled={isAir} />
<Tab label={minecraft['container.enchant']} disabled={isAir} />
<Tab label='NBT' disabled={isAir} />
</Tabs>
{nbt && tab === 0 && <Grid container spacing={1} rowSpacing={1}>
<Grid item xs={12} md={6}><TextField
fullWidth
label={lang.itemEditor.count}
type='number'
variant='standard'
value={nbt.Count}
disabled={isAir}
onChange={e => {
nbt.Count = new Byte(item!.amount = parseInt(e.target.value))
update()
}}
/></Grid>
<Grid item xs={12} md={6}><TextField
fullWidth
label={lang.itemEditor.damage}
type='number'
variant='standard'
value={nbt.tag?.Damage}
disabled={isAir}
onChange={e => {
set(nbt, 'tag.Damage', parseInt(e.target.value))
update()
}}
/></Grid>
<Grid item xs={12} md={6}>
<TextField
fullWidth
label={lang.itemEditor.displayName}
variant='standard'
disabled={isAir}
value={name ? stringifyTextComponent(JSON.parse(name)) : ''}
onChange={e => {
set(nbt, 'tag.display.Name', JSON.stringify(item!.name = e.target.value))
update()
}}
/>
<FormControlLabel
label={minecraft['item.unbreakable']}
disabled={isAir}
checked={nbt.tag?.Unbreakable?.value === 1}
control={<Checkbox
checked={nbt.tag?.Unbreakable?.value === 1}
onChange={e => {
set(nbt, 'tag.Unbreakable', new Byte(+e.target.checked))
update()
}} />
}
/>
</Grid>
<Grid item xs={12} md={6}><TextField
fullWidth
multiline
label={lang.itemEditor.lore}
variant='standard'
maxRows={5}
disabled={isAir}
value={nbt.tag?.display?.Lore?.map(l => stringifyTextComponent(JSON.parse(l)))?.join('\n') || ''}
onChange={e => {
set(nbt, 'tag.display.Lore', e.target.value.split('\n').map(text => JSON.stringify(text)))
update()
}}
/></Grid>
</Grid>}
{nbt && tab === 1 && <Grid container spacing={1} sx={{ width: '100%' }}>
{nbt.tag?.Enchantments?.map((it, i) => {
enchantmentMap[it.id] = true
return <Grid item key={i}><Chip label={getEnchantmentName(it)} onDelete={() => {
nbt?.tag?.Enchantments?.splice(i, 1)
update()
}} /></Grid>
})}
<Grid item><Chip label={lang.itemEditor.newEnchantment} color='primary' onClick={() => {
setEnchantment('')
setLevel(1)
}} /></Grid>
<Dialog onClose={() => setEnchantment(undefined)} open={enchantment != null}>
<DialogTitle>{lang.itemEditor.newEnchantmentTitle}</DialogTitle>
<DialogContent>
<Box component='form' sx={{ display: 'flex', flexWrap: 'wrap' }}>
<FormControl variant='standard' sx={{ m: 1, minWidth: 120 }}>
<InputLabel htmlFor='item-editor-enchantment-selector'>{minecraft['container.enchant']}</InputLabel>
<Select
id='item-editor-enchantment-selector'
label={minecraft['container.enchant']}
value={enchantment || ''}
onChange={e => setEnchantment(e.target.value)}
>{enchantments
.filter(e => !(e in enchantmentMap))
.map(it => <MenuItem key={it} value={it}>{getEnchantmentName(it)}</MenuItem>)}
</Select>
</FormControl>
<FormControl sx={{ m: 1, minWidth: 120 }}>
<TextField
label={lang.itemEditor.level}
type='number'
variant='standard'
value={level}
onChange={e => setLevel(parseInt(e.target.value))}
/>
</FormControl>
</Box>
</DialogContent>
<DialogActions>
<Button onClick={() => setEnchantment(undefined)}>{minecraft['gui.cancel']}</Button>
<Button disabled={!enchantment || isNaN(level)} onClick={() => {
if (nbt) {
if (!nbt.tag) nbt.tag = { Damage: new Int(0) }
;(nbt.tag.Enchantments || (nbt.tag.Enchantments = [])).push({ id: enchantment!, lvl: new Short(level) })
}
setEnchantment(undefined)
update()
}}>{minecraft['gui.ok']}</Button>
</DialogActions>
</Dialog>
</Grid>}
</DialogContent>
{nbt && tab === 2 && <Box sx={{
'& .CodeMirror': { width: '100%' },
'& .CodeMirror-dialog, .CodeMirror-scrollbar-filler': { backgroundColor: theme.palette.background.paper + '!important' }
}}>
<UnControlled
value={nbtText}
options={{
mode: 'javascript',
phrases: lang.codeMirrorPhrases,
theme: theme.palette.mode === 'dark' ? 'material' : 'one-light'
}}
onChange={(_: any, __: any, nbt: string) => {
const n = parse(nbt) as any as NBT
const newItem: any = { ...item, nbt }
if (n.Count?.value != null) newItem.amount = n.Count.value
setItem(newItem)
}}
/>
</Box>}
<DialogActions>
<Button onClick={cancel}>{minecraft['gui.cancel']}</Button>
<Button onClick={() => {
setItem(undefined)
if (_resolve) {
_resolve(!item || item.type === 'AIR' ? null : item)
_resolve = null
}
}}>{minecraft['gui.ok']}</Button>
</DialogActions>
</Dialog>
}
Example #23
Source File: DetailsModal.tsx From frontend with MIT License | 4 votes |
function DetailsModal() {
const { getDialogs } = DialogStore
const handleClose = () => DialogStore.hide()
const { t } = useTranslation()
return (
<>
{getDialogs.map(({ id, show, title, row }) => {
return (
<Dialog
key={id}
onClose={handleClose}
open={show}
maxWidth="md"
PaperProps={{ elevation: 5 }}
BackdropProps={{ style: { opacity: 0.3 } }}>
{title && <DialogTitle>{title}</DialogTitle>}
<DialogContent dividers>
{/* TODO: Extract concrete implementation and use generic one */}
<Grid item xs={12}>
<List>
<ListItem>
<ListItemText
primary={`${row.getValue(row.id, 'name')}`}
secondary={row.row.person.company}
/>
</ListItem>
<ListItem>
<ListItemText primary={row.row.person.email} secondary={row.row.person.phone} />
</ListItem>
<ListItem>{dateFormatter(row.row.createdAt)}</ListItem>
<ListItem>
<Typography variant="body2">{row.row.message || row.row.comment}</Typography>
</ListItem>
<ListItem>
<Typography variant="caption">{row.row.person.id}</Typography>
</ListItem>
{'associationMember' in row.row &&
[
'associationMember',
'benefactorCampaign',
'benefactorPlatform',
'companyOtherText',
'companySponsor',
'companyVolunteer',
'partnerBussiness',
'partnerNpo',
'partnerOtherText',
'roleAssociationMember',
'roleBenefactor',
'roleCompany',
'rolePartner',
'roleVolunteer',
'volunteerBackend',
'volunteerDesigner',
'volunteerDevOps',
'volunteerFinancesAndAccounts',
'volunteerFrontend',
'volunteerLawyer',
'volunteerMarketing',
'volunteerProjectManager',
'volunteerQa',
'volunteerSecurity',
].map((k, i) => (
<ListItem key={i}>
<ListItemText
primary={k}
secondary={row.row[k] ? <Check color="primary" /> : <Clear />}
/>
</ListItem>
))}
</List>
</Grid>
{/* */}
</DialogContent>
<DialogActions>
<Button autoFocus onClick={handleClose} color="primary">
{t('common:close')}
</Button>
</DialogActions>
</Dialog>
)
})}
</>
)
}
Example #24
Source File: ConfirmDialog.tsx From console with GNU Affero General Public License v3.0 | 4 votes |
ConfirmDialog = ({
isOpen = false,
onClose,
onCancel,
onConfirm,
classes = {},
title = "",
isLoading,
confirmationContent,
cancelText = "Cancel",
confirmText = "Confirm",
confirmButtonProps = {},
cancelButtonProps = {},
titleIcon = null,
}: ConfirmDialogProps) => {
return (
<Dialog
open={isOpen}
onClose={(event, reason) => {
if (reason !== "backdropClick") {
onClose(); // close on Esc but not on click outside
}
}}
className={classes.root}
sx={{
"& .MuiPaper-root": {
padding: "1rem 2rem 2rem 1rem",
},
}}
>
<DialogTitle className={classes.title}>
<div className={classes.titleText}>
{titleIcon} {title}
</div>
<div className={classes.closeContainer}>
<IconButton
aria-label="close"
className={classes.closeButton}
onClick={onClose}
disableRipple
size="small"
>
<CloseIcon />
</IconButton>
</div>
</DialogTitle>
<DialogContent className={classes.content}>
{confirmationContent}
</DialogContent>
<DialogActions className={classes.actions}>
<Button
className={classes.cancelButton}
onClick={onCancel || onClose}
disabled={isLoading}
type="button"
{...cancelButtonProps}
variant="outlined"
color="primary"
id={"confirm-cancel"}
>
{cancelText}
</Button>
<LoadingButton
className={classes.confirmButton}
type="button"
onClick={onConfirm}
loading={isLoading}
disabled={isLoading}
variant="outlined"
color="secondary"
loadingPosition="start"
startIcon={<React.Fragment />}
autoFocus
id={"confirm-ok"}
{...confirmButtonProps}
>
{confirmText}
</LoadingButton>
</DialogActions>
</Dialog>
);
}
Example #25
Source File: GridColumns.tsx From mui-toolpad with MIT License | 4 votes |
function GridColumnsPropEditor({
label,
nodeId,
value = [],
onChange,
disabled,
}: EditorProps<GridColumns>) {
const { bindings } = usePageEditorState();
const [editColumnsDialogOpen, setEditColumnsDialogOpen] = React.useState(false);
const [editedIndex, setEditedIndex] = React.useState<number | null>(null);
const editedColumn = typeof editedIndex === 'number' ? value[editedIndex] : null;
React.useEffect(() => {
if (editColumnsDialogOpen) {
setEditedIndex(null);
}
}, [editColumnsDialogOpen]);
const [menuAnchorEl, setMenuAnchorEl] = React.useState<null | HTMLElement>(null);
const menuOpen = Boolean(menuAnchorEl);
const handleMenuClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setMenuAnchorEl(event.currentTarget);
};
const handleClose = () => {
setMenuAnchorEl(null);
};
const rowsValue = nodeId && bindings[`${nodeId}.props.rows`];
const definedRows: unknown = rowsValue?.value;
const columnSuggestions = React.useMemo(() => {
const inferred = inferColumns(Array.isArray(definedRows) ? definedRows : []);
const existingFields = new Set(value.map(({ field }) => field));
return inferred.filter((column) => !existingFields.has(column.field));
}, [definedRows, value]);
const handleCreateColumn = React.useCallback(
(suggestion: GridColDef) => () => {
const existingFields = new Set(value.map(({ field }) => field));
const newFieldName = generateUniqueString(suggestion.field, existingFields);
const newValue = [...value, { ...suggestion, field: newFieldName }];
onChange(newValue);
setEditedIndex(newValue.length - 1);
handleClose();
},
[value, onChange],
);
const handleColumnItemClick = React.useCallback(
(index: number) => () => {
setEditedIndex(index);
},
[],
);
const handleColumnChange = React.useCallback(
(newValue: GridColDef) => {
onChange(value.map((column, i) => (i === editedIndex ? newValue : column)));
},
[editedIndex, onChange, value],
);
const handleColumnDelete = React.useCallback(
(deletedIndex: number) => (event: React.MouseEvent) => {
event.stopPropagation();
onChange(value.filter((column, i) => i !== deletedIndex));
},
[onChange, value],
);
return (
<React.Fragment>
<Button onClick={() => setEditColumnsDialogOpen(true)}>{label}</Button>
<Dialog
fullWidth
open={editColumnsDialogOpen}
onClose={() => setEditColumnsDialogOpen(false)}
>
{editedColumn ? (
<React.Fragment>
<DialogTitle>
<IconButton aria-label="Back" onClick={() => setEditedIndex(null)}>
<ArrowBackIcon />
</IconButton>
Edit column {editedColumn.field}
</DialogTitle>
<DialogContent>
<Stack gap={1} py={1}>
<TextField
label="field"
value={editedColumn.field}
disabled={disabled}
onChange={(event) =>
handleColumnChange({ ...editedColumn, field: event.target.value })
}
/>
<TextField
select
fullWidth
label="type"
value={editedColumn.type ?? ''}
disabled={disabled}
onChange={(event) =>
handleColumnChange({ ...editedColumn, type: event.target.value })
}
>
{COLUMN_TYPES.map((type) => (
<MenuItem key={type} value={type}>
{type}
</MenuItem>
))}
</TextField>
<TextField
select
fullWidth
label="align"
value={editedColumn.align ?? ''}
disabled={disabled}
onChange={(event) =>
handleColumnChange({
...editedColumn,
align: (event.target.value as GridAlignment) || undefined,
})
}
>
{ALIGNMENTS.map((alignment) => (
<MenuItem key={alignment} value={alignment}>
{alignment}
</MenuItem>
))}
</TextField>
<TextField
label="width"
type="number"
value={editedColumn.width}
disabled={disabled}
onChange={(event) =>
handleColumnChange({ ...editedColumn, width: Number(event.target.value) })
}
/>
</Stack>
</DialogContent>
</React.Fragment>
) : (
<React.Fragment>
<DialogTitle>Edit columns</DialogTitle>
<DialogContent>
<IconButton aria-label="Add column" onClick={handleMenuClick} disabled={disabled}>
<AddIcon />
</IconButton>
<Menu
id="new-column-menu"
anchorEl={menuAnchorEl}
open={menuOpen}
onClose={handleClose}
MenuListProps={{
'aria-labelledby': 'basic-button',
}}
>
{columnSuggestions.map((suggestion) => (
<MenuItem key={suggestion.field} onClick={handleCreateColumn(suggestion)}>
{suggestion.field}
</MenuItem>
))}
<MenuItem onClick={handleCreateColumn({ field: 'new' })}>new column</MenuItem>
</Menu>
<List>
{value.map((colDef, i) => {
return (
<ListItem
key={colDef.field}
disableGutters
onClick={handleColumnItemClick(i)}
secondaryAction={
<IconButton
aria-label="Remove column"
edge="end"
onClick={handleColumnDelete(i)}
>
<DeleteIcon />
</IconButton>
}
>
<ListItemButton>
<ListItemText primary={colDef.field} />
</ListItemButton>
</ListItem>
);
})}
</List>
</DialogContent>
</React.Fragment>
)}
<DialogActions>
<Button color="inherit" variant="text" onClick={() => setEditColumnsDialogOpen(false)}>
Close
</Button>
</DialogActions>
</Dialog>
</React.Fragment>
);
}
Example #26
Source File: TransactionCreateModal.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function TransactionCreateModal({ group, show, onClose }) {
const setTransactions = useSetRecoilState(groupTransactions(group.id));
const handleSubmit = (values, { setSubmitting }) => {
createTransaction({
groupID: group.id,
type: values.type,
description: values.description,
value: parseFloat(values.value),
billedAt: values.billedAt.toISODate(),
currencySymbol: "€",
currencyConversionRate: 1.0,
})
.then((t) => {
addTransactionInState(t, setTransactions);
setSubmitting(false);
onClose();
})
.catch((err) => {
toast.error(err);
setSubmitting(false);
});
};
const validate = (values) => {
let errors = { value: undefined, description: undefined, billedAt: undefined };
const floatValue = parseFloat(values.value);
if (isNaN(floatValue) || floatValue <= 0) {
errors.value = "please input a valid decimal number";
}
if (values.description === null || values.description === undefined || values.description === "") {
errors.description = "please input a description";
}
if (values.billedAt === null || values.billedAt === undefined || values.billedAt === "") {
errors.billedAt = "please input valid billed at time";
}
return errors;
};
return (
<Dialog open={show} onClose={onClose}>
<DialogTitle>Create Transaction</DialogTitle>
<DialogContent>
<Formik
validate={validate}
initialValues={{
type: "purchase",
description: "",
value: "0.0",
billedAt: DateTime.now(),
}}
onSubmit={handleSubmit}
>
{({ values, setFieldValue, handleChange, handleBlur, handleSubmit, isSubmitting }) => (
<Form>
<Select
required
value={values.type}
onChange={handleChange}
onBlur={handleBlur}
variant="standard"
name="type"
>
<MenuItem value="purchase">Purchase</MenuItem>
<MenuItem value="transfer">Transfer</MenuItem>
{/*<MenuItem value="mimo">MIMO</MenuItem>*/}
</Select>
<TextField
margin="normal"
required
fullWidth
variant="standard"
name="description"
label="Description"
autoFocus
value={values.description}
onChange={handleChange}
onBlur={handleBlur}
/>
<DatePicker
inputFormat="yyyy-MM-dd"
renderInput={(params) => (
<TextField
name="billedAt"
required
variant="standard"
fullWidth
{...params}
helperText={null}
/>
)}
label="Billed at"
value={values.billedAt}
onChange={(val) => setFieldValue("billedAt", val, true)}
/>
<TextField
margin="normal"
required
fullWidth
type="number"
variant="standard"
name="value"
label="Value"
value={values.value}
onChange={handleChange}
onBlur={handleBlur}
/>
{isSubmitting && <LinearProgress />}
<DialogActions>
<Button type="submit" color="primary" disabled={isSubmitting}>
Save
</Button>
<Button color="error" onClick={onClose}>
Cancel
</Button>
</DialogActions>
</Form>
)}
</Formik>
</DialogContent>
</Dialog>
);
}
Example #27
Source File: ExportButton.tsx From firecms with MIT License | 4 votes |
export function ExportButton<M extends { [Key: string]: any }, UserType>({
schema,
schemaResolver,
path,
exportConfig
}: ExportButtonProps<M, UserType>
) {
const dataSource = useDataSource();
const context = useFireCMSContext();
const dataRef = useRef<Entity<M>[]>();
const additionalDataRef = useRef<Record<string, any>[]>();
const [dataLoading, setDataLoading] = React.useState<boolean>(false);
const [dataLoadingError, setDataLoadingError] = React.useState<Error | undefined>();
// If in the initial load, we get more than INITIAL_DOCUMENTS_LIMIT results
const [hasLargeAmountOfData, setHasLargeAmountOfData] = React.useState<boolean>(false);
// did the user agree to export a large amount of data
const [fetchLargeDataAccepted, setFetchLargeDataAccepted] = React.useState<boolean>(false);
const [open, setOpen] = React.useState(false);
const handleClickOpen = useCallback(() => {
setOpen(true);
}, [setOpen]);
const handleClose = useCallback(() => {
setOpen(false);
}, [setOpen]);
const doDownload = useCallback((data: Entity<M>[] | undefined,
additionalData: Record<string, any>[] | undefined,
schema: EntitySchema<M>,
schemaResolver: EntitySchemaResolver<M>,
path: string,
exportConfig: ExportConfig | undefined) => {
if (!data)
throw Error("Trying to perform export without loading data first");
const resolvedSchema = schemaResolver({});
downloadCSV(data, additionalData, resolvedSchema, path, exportConfig);
}, []);
useEffect(() => {
if (!open) return;
setDataLoading(true);
const updateEntities = async (entities: Entity<M>[]) => {
if (entities.length >= INITIAL_DOCUMENTS_LIMIT) {
setHasLargeAmountOfData(true);
}
const pendingDownload = dataRef.current && entities.length > dataRef.current.length && fetchLargeDataAccepted;
dataRef.current = entities;
const additionalColumnsData = await fetchAdditionalColumns(entities);
additionalDataRef.current = additionalColumnsData;
setDataLoading(false);
setDataLoadingError(undefined);
if (pendingDownload) {
doDownload(entities, additionalColumnsData, schema, schemaResolver, path, exportConfig);
handleClose();
}
};
const fetchAdditionalColumns = async (entities: Entity<M>[]) => {
if (!exportConfig?.additionalColumns) {
return;
}
const additionalColumns = exportConfig.additionalColumns;
const resolvedColumnsValues: Record<string, any>[] = await Promise.all(entities.map(async (entity) => {
return (await Promise.all(additionalColumns.map(async (column) => {
return {
[column.key]: await column.builder({
entity,
context
})
};
}))).reduce((a, b) => ({ ...a, ...b }), {});
}));
return resolvedColumnsValues;
};
const onFetchError = (error: Error) => {
console.error("ERROR", error);
setDataLoading(false);
setDataLoadingError(error);
};
dataSource.fetchCollection<M>({
path,
schema: schemaResolver,
limit: fetchLargeDataAccepted ? undefined : INITIAL_DOCUMENTS_LIMIT
})
.then(updateEntities)
.catch(onFetchError);
}, [path, fetchLargeDataAccepted, schema, open, dataSource, schemaResolver, doDownload, exportConfig, handleClose, context]);
const needsToAcceptFetchAllData = hasLargeAmountOfData && !fetchLargeDataAccepted;
const onOkClicked = useCallback(() => {
if (needsToAcceptFetchAllData) {
setFetchLargeDataAccepted(true);
} else {
doDownload(dataRef.current, additionalDataRef.current, schema, schemaResolver, path, exportConfig);
handleClose();
}
}, [needsToAcceptFetchAllData, doDownload, schema, schemaResolver, path, exportConfig, handleClose]);
return <>
<Tooltip title={"Export"}>
<IconButton color={"primary"} onClick={handleClickOpen}
size="large">
<GetAppIcon/>
</IconButton>
</Tooltip>
<Dialog
open={open}
onClose={handleClose}
>
<DialogTitle>Export data</DialogTitle>
<DialogContent>
<DialogContentText>
<div>Download the the content of this table as a CSV
</div>
<br/>
{needsToAcceptFetchAllData &&
<Alert elevation={1}
variant="filled"
severity={"warning"}>
<div>
This collections has a large number
of documents (more than {INITIAL_DOCUMENTS_LIMIT}).
</div>
<div>
Would you like to proceed?
</div>
</Alert>}
</DialogContentText>
</DialogContent>
<DialogActions>
{dataLoading && <CircularProgress size={16} thickness={8}/>}
<Button color="primary" onClick={handleClose}>
Cancel
</Button>
<Button color="primary"
disabled={dataLoading}
onClick={onOkClicked}>
Download
</Button>
</DialogActions>
</Dialog>
</>;
}
Example #28
Source File: index.tsx From ExpressLRS-Configurator with GNU General Public License v3.0 | 4 votes |
ConfiguratorView: FunctionComponent<ConfiguratorViewProps> = (props) => {
const {
gitRepository,
selectedDevice,
networkDevices,
onDeviceChange,
deviceType,
} = props;
const [viewState, setViewState] = useState<ViewState>(
ViewState.Configuration
);
const { setAppStatus } = useAppState();
const [progressNotifications, setProgressNotifications] = useState<
BuildProgressNotification[]
>([]);
const progressNotificationsRef = useRef<BuildProgressNotification[]>([]);
const [
lastProgressNotification,
setLastProgressNotification,
] = useState<BuildProgressNotification | null>(null);
useBuildProgressNotificationsSubscription({
onSubscriptionData: (options) => {
const args = options.subscriptionData.data?.buildProgressNotifications;
if (args !== undefined) {
const newNotificationsList = [
...progressNotificationsRef.current,
args,
];
progressNotificationsRef.current = newNotificationsList;
setProgressNotifications(newNotificationsList);
setLastProgressNotification(args);
}
},
});
/*
We batch log events in order to save React.js state updates and rendering performance.
*/
const [logs, setLogs] = useState<string>('');
const logsRef = useRef<string[]>([]);
const eventsBatcherRef = useRef<EventsBatcher<string> | null>(null);
useEffect(() => {
eventsBatcherRef.current = new EventsBatcher<string>(200);
eventsBatcherRef.current.onBatch((newLogs) => {
const newLogsList = [...logsRef.current, ...newLogs];
logsRef.current = newLogsList;
setLogs(newLogsList.join(''));
});
}, []);
useBuildLogUpdatesSubscription({
fetchPolicy: 'network-only',
onSubscriptionData: (options) => {
const args = options.subscriptionData.data?.buildLogUpdates.data;
if (args !== undefined && eventsBatcherRef.current !== null) {
eventsBatcherRef.current.enqueue(args);
}
},
});
const [
firmwareVersionData,
setFirmwareVersionData,
] = useState<FirmwareVersionDataInput | null>(null);
const [firmwareVersionErrors, setFirmwareVersionErrors] = useState<Error[]>(
[]
);
const onFirmwareVersionData = useCallback(
(data: FirmwareVersionDataInput) => {
setFirmwareVersionErrors([]);
setFirmwareVersionData(data);
},
[]
);
const [deviceTarget, setDeviceTarget] = useState<Target | null>(null);
const [deviceTargetErrors, setDeviceTargetErrors] = useState<Error[]>([]);
const onDeviceTarget = useCallback(
(data: Target | null) => {
setDeviceTargetErrors([]);
setDeviceTarget(data);
// if target was manually changed, set selected device to null
onDeviceChange(null);
},
[onDeviceChange]
);
const [deviceTargets, setDeviceTargets] = useState<Device[] | null>(null);
const [
fetchDeviceTargets,
{
loading: loadingTargets,
data: targetsResponse,
error: targetsResponseError,
},
] = useAvailableFirmwareTargetsLazyQuery({
fetchPolicy: 'network-only',
});
const [
fetchLuaScript,
{ data: luaScriptResponse, error: luaScriptResponseError },
] = useLuaScriptLazyQuery();
const device = useMemo(() => {
return deviceTargets?.find((d) => {
return d.targets.find((target) => target.id === deviceTarget?.id);
});
}, [deviceTarget, deviceTargets]);
useEffect(() => {
if (
firmwareVersionData === null ||
validateFirmwareVersionData(firmwareVersionData).length > 0
) {
setDeviceTargets(null);
} else {
fetchDeviceTargets({
variables: {
source: firmwareVersionData.source as FirmwareSource,
gitBranch: firmwareVersionData.gitBranch!,
gitTag: firmwareVersionData.gitTag!,
gitCommit: firmwareVersionData.gitCommit!,
localPath: firmwareVersionData.localPath!,
gitPullRequest: firmwareVersionData.gitPullRequest,
gitRepository: {
url: gitRepository.url,
owner: gitRepository.owner,
repositoryName: gitRepository.repositoryName,
rawRepoUrl: gitRepository.rawRepoUrl,
srcFolder: gitRepository.srcFolder,
},
},
});
}
}, [gitRepository, firmwareVersionData, fetchDeviceTargets]);
useEffect(() => {
if (targetsResponse?.availableFirmwareTargets) {
setDeviceTargets([...targetsResponse.availableFirmwareTargets]);
} else {
setDeviceTargets(null);
}
}, [targetsResponse]);
const [
deviceOptionsFormData,
setDeviceOptionsFormData,
] = useState<DeviceOptionsFormData>({
userDefinesTxt: '',
userDefinesMode: UserDefinesMode.UserInterface,
userDefineOptions: [],
});
const handleDeviceOptionsResponse = async (
deviceOptionsResponse: TargetDeviceOptionsQuery
) => {
const storage = new ApplicationStorage();
const deviceName = device?.name || null;
const userDefineOptions = await mergeWithDeviceOptionsFromStorage(
storage,
deviceName,
{
...deviceOptionsFormData,
userDefineOptions: [...deviceOptionsResponse.targetDeviceOptions],
}
);
// if a network device is selected, merge in its options
if (selectedDevice && networkDevices.has(selectedDevice)) {
const networkDevice = networkDevices.get(selectedDevice);
userDefineOptions.userDefineOptions = userDefineOptions.userDefineOptions.map(
(userDefineOption) => {
const networkDeviceOption = networkDevice?.options.find(
(item) => item.key === userDefineOption.key
);
const newUserDefineOption = { ...userDefineOption };
if (networkDeviceOption) {
newUserDefineOption.enabled = networkDeviceOption.enabled;
newUserDefineOption.value = networkDeviceOption.value;
}
return newUserDefineOption;
}
);
}
setDeviceOptionsFormData(userDefineOptions);
};
const [
fetchOptions,
{
loading: loadingOptions,
data: deviceOptionsResponse,
error: deviceOptionsResponseError,
},
] = useTargetDeviceOptionsLazyQuery({
fetchPolicy: 'network-only',
onCompleted: (data) => {
handleDeviceOptionsResponse(data).catch((err) => {
console.error('failed to handle device options response', err);
});
},
});
useEffect(() => {
if (
deviceTarget === null ||
firmwareVersionData === null ||
validateFirmwareVersionData(firmwareVersionData).length > 0
) {
setDeviceOptionsFormData({
userDefinesTxt: '',
userDefinesMode: UserDefinesMode.UserInterface,
userDefineOptions: [],
});
} else {
fetchOptions({
variables: {
target: deviceTarget.name,
source: firmwareVersionData.source as FirmwareSource,
gitBranch: firmwareVersionData.gitBranch!,
gitTag: firmwareVersionData.gitTag!,
gitCommit: firmwareVersionData.gitCommit!,
localPath: firmwareVersionData.localPath!,
gitPullRequest: firmwareVersionData.gitPullRequest,
gitRepository: {
url: gitRepository.url,
owner: gitRepository.owner,
repositoryName: gitRepository.repositoryName,
rawRepoUrl: gitRepository.rawRepoUrl,
srcFolder: gitRepository.srcFolder,
},
},
});
}
}, [deviceTarget, firmwareVersionData, gitRepository, fetchOptions]);
const onResetToDefaults = () => {
const handleReset = async () => {
if (deviceOptionsResponse === undefined || deviceTarget === null) {
// eslint-disable-next-line no-alert
alert(`deviceOptionsResponse is undefined`);
return;
}
const deviceName = device?.name || null;
if (deviceName) {
const storage = new ApplicationStorage();
await storage.removeDeviceOptions(deviceName);
const userDefineOptions = await mergeWithDeviceOptionsFromStorage(
storage,
deviceName,
{
...deviceOptionsFormData,
userDefineOptions: [...deviceOptionsResponse.targetDeviceOptions],
}
);
setDeviceOptionsFormData(userDefineOptions);
}
};
handleReset().catch((err) => {
console.error(`failed to reset device options form data: ${err}`);
});
};
const onUserDefines = useCallback(
(data: DeviceOptionsFormData) => {
setDeviceOptionsFormData(data);
if (deviceTarget !== null) {
const storage = new ApplicationStorage();
const deviceName = device?.name;
if (deviceName) {
persistDeviceOptions(storage, deviceName, data).catch((err) => {
console.error(`failed to persist user defines: ${err}`);
});
}
}
},
[deviceTarget, deviceTargets]
);
const [
buildFlashFirmwareMutation,
{
loading: buildInProgress,
data: response,
error: buildFlashErrorResponse,
},
] = useBuildFlashFirmwareMutation();
useEffect(() => {
const arg = response?.buildFlashFirmware?.firmwareBinPath;
if (arg !== undefined && arg !== null && arg?.length > 0) {
const body: OpenFileLocationRequestBody = {
path: arg,
};
ipcRenderer.send(IpcRequest.OpenFileLocation, body);
}
}, [response]);
const isTX = useMemo(() => {
if (deviceTarget) {
return deviceTarget.name?.indexOf('_TX_') > -1;
}
return false;
}, [deviceTarget]);
const hasLuaScript = useMemo(() => {
return deviceType === DeviceType.ExpressLRS && isTX;
}, [deviceType, isTX]);
useEffect(() => {
if (firmwareVersionData && isTX && hasLuaScript) {
fetchLuaScript({
variables: {
source: firmwareVersionData.source as FirmwareSource,
gitBranch: firmwareVersionData.gitBranch!,
gitTag: firmwareVersionData.gitTag!,
gitCommit: firmwareVersionData.gitCommit!,
localPath: firmwareVersionData.localPath!,
gitPullRequest: firmwareVersionData.gitPullRequest,
gitRepository: {
url: gitRepository.url,
owner: gitRepository.owner,
repositoryName: gitRepository.repositoryName,
rawRepoUrl: gitRepository.rawRepoUrl,
srcFolder: gitRepository.srcFolder,
},
},
});
}
}, [gitRepository, firmwareVersionData, fetchLuaScript, isTX, hasLuaScript]);
/*
Display Electron.js confirmation dialog if user wants to shutdown the app
when build is in progress.
*/
useEffect(() => {
const body: UpdateBuildStatusRequestBody = {
buildInProgress,
};
ipcRenderer.send(IpcRequest.UpdateBuildStatus, body);
}, [buildInProgress]);
const [serialDevice, setSerialDevice] = useState<string | null>(null);
const onSerialDevice = (newSerialDevice: string | null) => {
setSerialDevice(newSerialDevice);
};
const [wifiDevice, setWifiDevice] = useState<string | null>(null);
const onWifiDevice = useCallback((newWifiDevice: string | null) => {
setWifiDevice(newWifiDevice);
}, []);
const [serialPortRequired, setSerialPortRequired] = useState<boolean>(false);
const [wifiDeviceRequired, setWifiDeviceRequired] = useState<boolean>(false);
useEffect(() => {
if (
deviceTarget &&
(deviceTarget.flashingMethod === FlashingMethod.BetaflightPassthrough ||
deviceTarget.flashingMethod === FlashingMethod.UART)
) {
setSerialPortRequired(true);
} else {
setSerialPortRequired(false);
}
if (deviceTarget && deviceTarget.flashingMethod === FlashingMethod.WIFI) {
setWifiDeviceRequired(true);
} else {
setWifiDeviceRequired(false);
}
}, [deviceTarget, deviceTarget, deviceTargets]);
const [
deviceOptionsValidationErrors,
setDeviceOptionsValidationErrors,
] = useState<Error[] | null>(null);
const reset = () => {
logsRef.current = [];
progressNotificationsRef.current = [];
setLogs('');
setFirmwareVersionErrors([]);
setDeviceTargetErrors([]);
setDeviceOptionsValidationErrors([]);
setProgressNotifications([]);
setLastProgressNotification(null);
};
const onBack = () => {
reset();
setViewState(ViewState.Configuration);
setAppStatus(AppStatus.Interactive);
};
const getAbbreviatedDeviceName = (item: Device) => {
return item.abbreviatedName?.slice(0, 16) ?? item.name?.slice(0, 16);
};
const [currentJobType, setCurrentJobType] = useState<BuildJobType>(
BuildJobType.Build
);
const sendJob = (type: BuildJobType) => {
reset();
setCurrentJobType(type);
// Validate firmware source
if (firmwareVersionData === null) {
setFirmwareVersionErrors([new Error('Please select firmware source')]);
return;
}
const sourceErrors = validateFirmwareVersionData(firmwareVersionData);
if (sourceErrors.length > 0) {
setFirmwareVersionErrors(sourceErrors);
return;
}
// Validate device target
if (deviceTarget === null) {
setDeviceTargetErrors([new Error('Please select a device target')]);
return;
}
// Validate device options
if (deviceOptionsFormData === null) {
setDeviceTargetErrors([
new Error('Please configure your device options'),
]);
return;
}
switch (deviceOptionsFormData.userDefinesMode) {
case UserDefinesMode.Manual:
break;
case UserDefinesMode.UserInterface:
const errs = new UserDefinesValidator().validate(
deviceOptionsFormData.userDefineOptions
);
if (errs.length > 0) {
setDeviceOptionsValidationErrors(errs);
return;
}
break;
default:
break;
}
let uploadPort: string | undefined;
if (serialPortRequired && serialDevice != null) {
uploadPort = serialDevice;
} else if (wifiDeviceRequired && wifiDevice !== null) {
uploadPort = wifiDevice;
}
const userDefines = deviceOptionsFormData.userDefineOptions.map((item) => ({
key: item.key,
value: item.value,
enabled: item.enabled,
enumValues: item.enumValues,
type: item.type,
}));
if (device?.parent && device?.name) {
const deviceName = getAbbreviatedDeviceName(device);
// add the user define for the device name
userDefines.push({
key: UserDefineKey.DEVICE_NAME,
value: deviceName,
enabled: true,
enumValues: null,
type: UserDefineKind.Text,
});
}
const input: BuildFlashFirmwareInput = {
type,
firmware: firmwareVersionData,
target: deviceTarget.name,
userDefinesTxt: deviceOptionsFormData.userDefinesTxt,
userDefinesMode: deviceOptionsFormData.userDefinesMode,
userDefines,
serialDevice: uploadPort,
};
buildFlashFirmwareMutation({
variables: {
input,
gitRepository: {
url: gitRepository.url,
owner: gitRepository.owner,
repositoryName: gitRepository.repositoryName,
rawRepoUrl: gitRepository.rawRepoUrl,
srcFolder: gitRepository.srcFolder,
},
},
});
setViewState(ViewState.Compiling);
setAppStatus(AppStatus.Busy);
};
useEffect(() => {
if (
!buildInProgress &&
response?.buildFlashFirmware?.success !== undefined
) {
window.scrollTo(0, document.body.scrollHeight);
}
}, [buildInProgress, response]);
const onBuild = () => sendJob(BuildJobType.Build);
const onBuildAndFlash = () => sendJob(BuildJobType.BuildAndFlash);
const onForceFlash = () => sendJob(BuildJobType.ForceFlash);
const deviceTargetRef = useRef<HTMLDivElement | null>(null);
const deviceOptionsRef = useRef<HTMLDivElement | null>(null);
const [
deviceSelectErrorDialogOpen,
setDeviceSelectErrorDialogOpen,
] = useState<boolean>(false);
const handleSelectedDeviceChange = useCallback(
(deviceName: string) => {
const dnsDevice = networkDevices.get(deviceName);
if (dnsDevice) {
const dnsDeviceName = dnsDevice.deviceName?.toUpperCase();
const dnsDeviceTarget = dnsDevice.target.toUpperCase();
let deviceMatches: Device[] | undefined = [];
// try to find the device by the deviceName
deviceMatches = deviceTargets?.filter((item) => {
return getAbbreviatedDeviceName(item).toUpperCase() === dnsDeviceName;
});
// if no matches found by deviceName, then use the target
if (
deviceMatches?.length === 0 &&
dnsDeviceTarget.trim().length !== 0
) {
deviceMatches = deviceTargets?.filter((item) => {
// only match on a device that doesn't have a parent, which means it
// is not an alias of another device
return (
!item.parent &&
item.targets.find((target) => {
const baseTargetName = target.name.split('_via_')[0];
return baseTargetName.toUpperCase() === dnsDeviceTarget;
})
);
});
}
// if no device is found that matches the target
if (!deviceMatches || deviceMatches.length === 0) {
console.error(
`no device matches found for target ${dnsDeviceTarget}!`
);
setDeviceSelectErrorDialogOpen(true);
return;
}
// if multiple device matches are found, then don't select any of them
// we do not know which one is correct and do not want to pick the wrong device.
if (deviceMatches.length > 1) {
console.error(
`multiple device matches found for target ${dnsDeviceTarget}!`
);
setDeviceSelectErrorDialogOpen(true);
return;
}
const deviceMatch = deviceMatches[0];
const dTarget =
deviceMatch?.targets.find((target) => {
return target.flashingMethod === FlashingMethod.WIFI;
}) ||
deviceMatch?.targets[0] ||
null;
if (dTarget !== deviceTarget) {
setDeviceTarget(dTarget);
deviceTargetRef?.current?.scrollIntoView({ behavior: 'smooth' });
}
setWifiDevice(dnsDevice.ip);
}
},
[deviceTarget, deviceTargets, networkDevices]
);
useEffect(() => {
if (selectedDevice) {
handleSelectedDeviceChange(selectedDevice);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDevice]);
const luaDownloadButton = () => {
if (
hasLuaScript &&
luaScriptResponse &&
luaScriptResponse.luaScript.fileLocation &&
luaScriptResponse.luaScript.fileLocation.length > 0
) {
return (
<Button
sx={styles.button}
color="primary"
size="large"
variant="contained"
href={luaScriptResponse?.luaScript.fileLocation ?? ''}
download
>
Download LUA script
</Button>
);
}
return null;
};
const handleDeviceSelectErrorDialogClose = useCallback(() => {
setDeviceSelectErrorDialogOpen(false);
}, []);
const saveBuildLogToFile = useCallback(async () => {
const saveFileRequestBody: SaveFileRequestBody = {
data: logs,
defaultPath: `ExpressLRSBuildLog_${new Date()
.toISOString()
.replace(/[^0-9]/gi, '')}.txt`,
};
const result: SaveFileResponseBody = await ipcRenderer.invoke(
IpcRequest.SaveFile,
saveFileRequestBody
);
if (result.success) {
const openFileLocationRequestBody: OpenFileLocationRequestBody = {
path: result.path,
};
ipcRenderer.send(
IpcRequest.OpenFileLocation,
openFileLocationRequestBody
);
}
}, [logs]);
return (
<MainLayout>
{viewState === ViewState.Configuration && (
<>
<Card>
<CardTitle icon={<SettingsIcon />} title="Firmware version" />
<Divider />
<CardContent>
<FirmwareVersionForm
onChange={onFirmwareVersionData}
data={firmwareVersionData}
gitRepository={gitRepository}
/>
<ShowAlerts severity="error" messages={firmwareVersionErrors} />
</CardContent>
<Divider />
<CardTitle icon={<SettingsIcon />} title="Target" />
<Divider />
<CardContent ref={deviceTargetRef}>
{firmwareVersionData === null ||
(validateFirmwareVersionData(firmwareVersionData).length >
0 && (
<Alert severity="info">
<AlertTitle>Notice</AlertTitle>
Please select a firmware version first
</Alert>
))}
{!loadingTargets && !targetsResponseError && (
<DeviceTargetForm
currentTarget={deviceTarget}
onChange={onDeviceTarget}
firmwareVersionData={firmwareVersionData}
deviceOptions={deviceTargets}
/>
)}
<Loader loading={loadingTargets} />
{luaDownloadButton()}
{hasLuaScript && (
<ShowAlerts
severity="error"
messages={luaScriptResponseError}
/>
)}
<ShowAlerts severity="error" messages={targetsResponseError} />
<ShowAlerts severity="error" messages={deviceTargetErrors} />
</CardContent>
<Divider />
<CardTitle
icon={<SettingsIcon />}
title={
<div ref={deviceOptionsRef}>
Device options{' '}
{deviceOptionsFormData.userDefinesMode ===
UserDefinesMode.UserInterface &&
deviceTarget !== null &&
!loadingOptions && (
<Tooltip
placement="top"
arrow
title={
<div>
Reset device options to the recommended defaults on
this device target. Except for your custom binding
phrase.
</div>
}
>
<Button onClick={onResetToDefaults} size="small">
Reset
</Button>
</Tooltip>
)}
</div>
}
/>
<Divider />
<CardContent>
{!loadingOptions && (
<DeviceOptionsForm
target={deviceTarget?.name ?? null}
deviceOptions={deviceOptionsFormData}
firmwareVersionData={firmwareVersionData}
onChange={onUserDefines}
/>
)}
{deviceOptionsFormData.userDefinesMode ===
UserDefinesMode.UserInterface &&
(firmwareVersionData === null ||
validateFirmwareVersionData(firmwareVersionData).length > 0 ||
deviceTarget === null) && (
<Alert severity="info">
<AlertTitle>Notice</AlertTitle>
Please select a firmware version and device target first
</Alert>
)}
<ShowAlerts
severity="error"
messages={deviceOptionsResponseError}
/>
<ShowAlerts
severity="error"
messages={deviceOptionsValidationErrors}
/>
<Loader loading={loadingOptions} />
</CardContent>
<Divider />
<CardTitle icon={<SettingsIcon />} title="Actions" />
<Divider />
<CardContent>
<UserDefinesAdvisor
deviceOptionsFormData={deviceOptionsFormData}
/>
<div>
{serialPortRequired && (
<SerialDeviceSelect
serialDevice={serialDevice}
onChange={onSerialDevice}
/>
)}
{wifiDeviceRequired && (
<WifiDeviceSelect
wifiDevice={wifiDevice}
wifiDevices={Array.from(networkDevices.values()).filter(
(item) => {
return deviceTarget?.name
?.toUpperCase()
.startsWith(item.target.toUpperCase());
}
)}
onChange={onWifiDevice}
/>
)}
<Button
sx={styles.button}
size="large"
variant="contained"
onClick={onBuild}
>
Build
</Button>
{deviceTarget?.flashingMethod !== FlashingMethod.Radio && (
<SplitButton
sx={styles.button}
size="large"
variant="contained"
options={[
{
label: 'Build & Flash',
value: BuildJobType.BuildAndFlash,
},
{
label: 'Force Flash',
value: BuildJobType.ForceFlash,
},
]}
onButtonClick={(value: string | null) => {
if (value === BuildJobType.BuildAndFlash) {
onBuildAndFlash();
} else if (value === BuildJobType.ForceFlash) {
onForceFlash();
}
}}
/>
)}
</div>
</CardContent>
</Card>
<Card>
{networkDevices.size > 0 && (
<Box>
<Divider />
<CardTitle icon={<NetworkWifi />} title="Network Devices" />
<Divider />
<CardContent>
<div>
<WifiDeviceList
wifiDevices={Array.from(networkDevices.values())}
onChange={(dnsDevice: MulticastDnsInformation) => {
onDeviceChange(dnsDevice);
handleSelectedDeviceChange(dnsDevice.name);
}}
/>
</div>
</CardContent>
</Box>
)}
</Card>
<Dialog
open={deviceSelectErrorDialogOpen}
onClose={handleDeviceSelectErrorDialogClose}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
Device Select Error
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
The target device could not be automatically selected, it must
be done manually.
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleDeviceSelectErrorDialogClose}>
Close
</Button>
</DialogActions>
</Dialog>
</>
)}
{viewState === ViewState.Compiling && (
<Card>
<CardTitle icon={<SettingsIcon />} title="Build" />
<Divider />
<CardContent>
<BuildProgressBar
inProgress={buildInProgress}
jobType={currentJobType}
progressNotification={lastProgressNotification}
/>
<BuildNotificationsList notifications={progressNotifications} />
<ShowAlerts severity="error" messages={buildFlashErrorResponse} />
</CardContent>
{logs.length > 0 && (
<>
<CardTitle
icon={<SettingsIcon />}
title={
<Box display="flex" justifyContent="space-between">
<Box>Logs</Box>
<Box>
<IconButton
aria-label="Copy log to clipboard"
title="Copy log to clipboard"
onClick={async () => {
await navigator.clipboard.writeText(logs);
}}
>
<ContentCopy />
</IconButton>
<IconButton
aria-label="Save log to file"
title="Save log to file"
onClick={saveBuildLogToFile}
>
<Save />
</IconButton>
</Box>
</Box>
}
/>
<Divider />
<CardContent>
<Box sx={styles.longBuildDurationWarning}>
<ShowTimeoutAlerts
severity="warning"
messages="Sometimes builds take at least a few minutes. It is normal, especially for the first time builds."
active={buildInProgress}
timeout={14 * 1000}
/>
</Box>
<Logs data={logs} />
</CardContent>
<Divider />
</>
)}
{response !== undefined && (
<>
<CardTitle icon={<SettingsIcon />} title="Result" />
<Divider />
<CardContent>
{response?.buildFlashFirmware?.success &&
currentJobType === BuildJobType.BuildAndFlash &&
deviceTarget?.flashingMethod === FlashingMethod.WIFI && (
<>
<Alert sx={styles.buildNotification} severity="warning">
<AlertTitle>Warning</AlertTitle>
Please wait for LED to resume blinking before
disconnecting power
</Alert>
</>
)}
<ShowAfterTimeout
timeout={
response?.buildFlashFirmware?.success &&
currentJobType === BuildJobType.BuildAndFlash &&
deviceTarget?.flashingMethod === FlashingMethod.WIFI
? 15000
: 1000
}
active={!buildInProgress}
>
<Box sx={styles.buildNotification}>
<BuildResponse
response={response?.buildFlashFirmware}
firmwareVersionData={firmwareVersionData}
/>
</Box>
{response?.buildFlashFirmware?.success && hasLuaScript && (
<>
<Alert sx={styles.buildNotification} severity="info">
<AlertTitle>Update Lua Script</AlertTitle>
Make sure to update the Lua script on your radio
</Alert>
</>
)}
</ShowAfterTimeout>
{response?.buildFlashFirmware?.success &&
currentJobType === BuildJobType.Build && (
<>
<Alert sx={styles.buildNotification} severity="info">
<AlertTitle>Build notice</AlertTitle>
{deviceTarget?.flashingMethod !== FlashingMethod.Radio
? 'Firmware binary file was opened in the file explorer'
: "Firmware binary file was opened in the file explorer, copy the firmware file to your radios's SD card and flash it to the transmitter using EdgeTX/OpenTX"}
</Alert>
</>
)}
</CardContent>
<Divider />
</>
)}
{!buildInProgress && (
<>
<CardTitle icon={<SettingsIcon />} title="Actions" />
<Divider />
<CardContent>
<Button
sx={styles.button}
color="primary"
size="large"
variant="contained"
onClick={onBack}
>
Back
</Button>
{!response?.buildFlashFirmware.success && (
<Button
sx={styles.button}
size="large"
variant="contained"
onClick={() => {
sendJob(currentJobType);
}}
>
Retry
</Button>
)}
{!response?.buildFlashFirmware.success &&
response?.buildFlashFirmware.errorType ===
BuildFirmwareErrorType.TargetMismatch && (
<Button
sx={styles.button}
size="large"
variant="contained"
onClick={onForceFlash}
>
Force Flash
</Button>
)}
{response?.buildFlashFirmware.success && luaDownloadButton()}
</CardContent>
</>
)}
</Card>
)}
</MainLayout>
);
}
Example #29
Source File: CreateAccountModal.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function CreateAccountModal({ show, onClose, group }) {
const setAccounts = useSetRecoilState(groupAccounts(group.id));
const userPermissions = useRecoilValue(currUserPermissions(group.id));
const currentUser = useRecoilValue(userData);
const handleSubmit = (values, { setSubmitting }) => {
createAccount({
groupID: group.id,
name: values.name,
owningUserID: values.owningUserID,
description: values.description,
})
.then((account) => {
toast.success(`Created account ${values.name}`);
addAccount(account, setAccounts);
setSubmitting(false);
onClose();
})
.catch((err) => {
toast.error(err);
setSubmitting(false);
});
};
return (
<Dialog open={show} onClose={onClose}>
<DialogTitle>Create Personal Account</DialogTitle>
<DialogContent>
<Formik
initialValues={{ name: "", description: "", owningUserID: null }}
onSubmit={handleSubmit}
validationSchema={validationSchema}
>
{({
values,
touched,
errors,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
setFieldValue,
}) => (
<Form>
<TextField
margin="normal"
required
fullWidth
autoFocus
variant="standard"
name="name"
label="Account Name"
onBlur={handleBlur}
onChange={handleChange}
value={values.name}
error={touched.name && Boolean(errors.name)}
helperText={touched.name && (errors.name as ReactNode)}
/>
<TextField
margin="normal"
name="description"
fullWidth
variant="standard"
label="Description"
onBlur={handleBlur}
onChange={handleChange}
value={values.description}
error={touched.description && Boolean(errors.description)}
helperText={touched.description && (errors.description as ReactNode)}
/>
{userPermissions.is_owner ? (
<GroupMemberSelect
margin="normal"
group={group}
label="Owning user"
value={values.owningUserID}
onChange={(user_id) => setFieldValue("owningUserID", user_id)}
/>
) : (
<FormControlLabel
control={
<Checkbox
name="owningUserID"
onChange={(e) =>
setFieldValue("owningUserID", e.target.checked ? currentUser.id : null)
}
checked={values.owningUserID === currentUser.id}
/>
}
label="This is me"
/>
)}
{isSubmitting && <LinearProgress />}
<DialogActions>
<Button type="submit" color="primary" disabled={isSubmitting}>
Save
</Button>
<Button color="error" onClick={onClose}>
Cancel
</Button>
</DialogActions>
</Form>
)}
</Formik>
</DialogContent>
</Dialog>
);
}