@mui/icons-material#Edit TypeScript Examples
The following examples show how to use
@mui/icons-material#Edit.
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: EditableField.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function EditableField({
value,
onChange,
validate = undefined,
helperText = undefined,
onStopEdit = undefined,
canEdit = true,
...props
}) {
const [currentValue, setValue] = useState("");
const [editing, setEditing] = useState(false);
const [error, setError] = useState(false);
const classes = useStyles();
useEffect(() => {
setValue(value);
}, [value]);
const onSave = () => {
if (!error) {
onChange(currentValue);
setValue("");
setEditing(false);
}
};
const startEditing = () => {
setValue(value);
setEditing(true);
};
const stopEditing = () => {
setValue(value);
setEditing(false);
if (onStopEdit) {
onStopEdit();
}
};
const onValueChange = (event) => {
setValue(event.target.value);
if (validate) {
setError(!validate(event.target.value));
}
};
const onKeyUp = (key) => {
if (key.keyCode === 13) {
onSave();
}
};
return (
<div className={classes.root}>
<DisabledTextField
error={error}
value={currentValue}
disabled={!editing}
onChange={onValueChange}
className={classes.input}
helperText={error ? helperText : null}
onKeyUp={onKeyUp}
{...props}
/>
{canEdit &&
(editing ? (
<>
<IconButton color="primary" onClick={onSave}>
<Check />
</IconButton>
<IconButton color="secondary" onClick={stopEditing}>
<Close />
</IconButton>
</>
) : (
<IconButton color="primary" onClick={startEditing}>
<Edit />
</IconButton>
))}
</div>
);
}
Example #2
Source File: TransactionActions.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function TransactionActions({ groupID, transaction }) {
const [confirmDeleteDialogOpen, setConfirmDeleteDialogOpen] = useState(false);
const history = useHistory();
const userPermissions = useRecoilValue(currUserPermissions(groupID));
const setTransactions = useSetRecoilState(groupTransactions(transaction.group_id));
const localTransactionChanges = useRecoilValue(pendingTransactionDetailChanges(transaction.id));
const localPositionChanges = useRecoilValue(pendingTransactionPositionChanges(transaction.id));
const resetLocalTransactionChanges = useResetRecoilState(pendingTransactionDetailChanges(transaction.id));
const resetLocalPositionChanges = useResetRecoilState(pendingTransactionPositionChanges(transaction.id));
const updateTransactionAndClearLocal = useRecoilTransaction_UNSTABLE(
({ get, set, reset }) =>
(transaction: TransactionBackend) => {
set(groupTransactions(transaction.group_id), (currTransactions) => {
return currTransactions.map((t) => (t.id === transaction.id ? transaction : t));
});
reset(pendingTransactionDetailChanges(transaction.id));
reset(pendingTransactionPositionChanges(transaction.id));
}
);
const edit = () => {
if (!transaction.is_wip) {
createTransactionChange({
transactionID: transaction.id,
})
.then((t) => {
updateTransactionAndClearLocal(t);
})
.catch((err) => {
toast.error(err);
});
}
};
const abortEdit = () => {
if (transaction.is_wip) {
if (transaction.has_committed_changes) {
discardTransactionChange({
transactionID: transaction.id,
})
.then((t) => {
updateTransactionAndClearLocal(t);
})
.catch((err) => {
toast.error(err);
});
} else {
history.push(`/groups/${groupID}/`);
}
}
};
const commitEdit = () => {
if (transaction.is_wip) {
// update the transaction given the currently pending changes
// find out which local changes we have and send them to da server
const positions = Object.values(localPositionChanges.modified)
.concat(
Object.values(localPositionChanges.added).map((position) => ({
...position,
id: -1,
}))
)
.map((p) => ({
id: p.id,
name: p.name,
communist_shares: p.communist_shares,
price: p.price,
usages: p.usages,
deleted: p.deleted,
}));
if (Object.keys(localTransactionChanges).length > 0) {
updateTransaction({
transactionID: transaction.id,
description: transaction.description,
value: transaction.value,
billedAt: transaction.billed_at,
currencySymbol: transaction.currency_symbol,
currencyConversionRate: transaction.currency_conversion_rate,
creditorShares: transaction.creditor_shares,
debitorShares: transaction.debitor_shares,
...localTransactionChanges,
positions: positions.length > 0 ? positions : null,
})
.then((t) => {
updateTransactionAndClearLocal(t);
})
.catch((err) => {
toast.error(err);
});
} else if (positions.length > 0) {
updateTransactionPositions({
transactionID: transaction.id,
positions: positions,
})
.then((t) => {
updateTransactionAndClearLocal(t);
})
.catch((err) => {
toast.error(err);
});
} else {
commitTransaction({ transactionID: transaction.id })
.then((t) => {
updateTransactionAndClearLocal(t);
})
.catch((err) => {
toast.error(err);
});
}
}
};
const confirmDeleteTransaction = () => {
deleteTransaction({ transactionID: transaction.id })
.then((t) => {
// TODO: use recoil transaction
updateTransactionInState(t, setTransactions);
resetLocalPositionChanges();
resetLocalTransactionChanges();
history.push(`/groups/${groupID}/`);
})
.catch((err) => {
toast.error(err);
});
};
return (
<>
<Grid container justifyContent="space-between">
<Grid item sx={{ display: "flex", alignItems: "center" }}>
<IconButton
sx={{ display: { xs: "none", md: "inline-flex" } }}
component={RouterLink}
to={`/groups/${groupID}/`}
>
<ChevronLeft />
</IconButton>
<Chip color="primary" label={transaction.type} />
</Grid>
<Grid item>
{userPermissions.can_write && (
<>
{transaction.is_wip ? (
<>
<Button color="primary" onClick={commitEdit}>
Save
</Button>
<Button color="error" onClick={abortEdit}>
Cancel
</Button>
</>
) : (
<IconButton color="primary" onClick={edit}>
<Edit />
</IconButton>
)}
<IconButton color="error" onClick={() => setConfirmDeleteDialogOpen(true)}>
<Delete />
</IconButton>
</>
)}
</Grid>
</Grid>
<Dialog maxWidth="xs" aria-labelledby="confirmation-dialog-title" open={confirmDeleteDialogOpen}>
<DialogTitle id="confirmation-dialog-title">Confirm delete transaction</DialogTitle>
<DialogContent dividers>
Are you sure you want to delete the transaction "{transaction.description}"
</DialogContent>
<DialogActions>
<Button autoFocus onClick={() => setConfirmDeleteDialogOpen(false)} color="primary">
Cancel
</Button>
<Button onClick={confirmDeleteTransaction} color="error">
Ok
</Button>
</DialogActions>
</Dialog>
</>
);
}
Example #3
Source File: AccountList.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function AccountList({ group }) {
const [speedDialOpen, setSpeedDialOpen] = useState(false);
const toggleSpeedDial = () => setSpeedDialOpen((currValue) => !currValue);
const [showPersonalAccountCreationModal, setShowPersonalAccountCreationModal] = useState(false);
const [showClearingAccountCreationModal, setShowClearingAccountCreationModal] = useState(false);
const [activeTab, setActiveTab] = useState("personal");
const [searchValuePersonal, setSearchValuePersonal] = useState("");
const [searchValueClearing, setSearchValueClearing] = useState("");
const [showPersonalAccountEditModal, setShowPersonalAccountEditModal] = useState(false);
const [showClearingAccountEditModal, setShowClearingAccountEditModal] = useState(false);
const [clearingAccountToCopy, setClearingAccountToCopy] = useState(undefined);
const [accountToEdit, setAccountToEdit] = useState(null);
const [clearingAccountToEdit, setClearingAccountToEdit] = useState(null);
const setAccounts = useSetRecoilState(groupAccounts(group.id));
const personalAccounts = useRecoilValue(personalAccountsSeenByUser(group.id));
const clearingAccounts = useRecoilValue(clearingAccountsSeenByUser(group.id));
const allAccounts = useRecoilValue(accountsSeenByUser(group.id));
const [accountToDelete, setAccountToDelete] = useState(null);
const userPermissions = useRecoilValue(currUserPermissions(group.id));
const currentUser = useRecoilValue(userData);
const memberIDToUsername = useRecoilValue(groupMemberIDsToUsername(group.id));
const [filteredPersonalAccounts, setFilteredPersonalAccounts] = useState([]);
const [filteredClearingAccounts, setFilteredClearingAccounts] = useState([]);
useEffect(() => {
if (searchValuePersonal != null && searchValuePersonal !== "") {
setFilteredPersonalAccounts(
personalAccounts.filter((t) => {
return (
t.name.toLowerCase().includes(searchValuePersonal.toLowerCase()) ||
t.description.toLowerCase().includes(searchValuePersonal.toLowerCase())
);
})
);
} else {
return setFilteredPersonalAccounts(personalAccounts);
}
}, [personalAccounts, searchValuePersonal, setFilteredPersonalAccounts]);
useEffect(() => {
if (searchValueClearing != null && searchValueClearing !== "") {
setFilteredClearingAccounts(
clearingAccounts.filter((t) => {
return (
t.name.toLowerCase().includes(searchValueClearing.toLowerCase()) ||
t.description.toLowerCase().includes(searchValueClearing.toLowerCase())
);
})
);
} else {
return setFilteredClearingAccounts(clearingAccounts);
}
}, [clearingAccounts, searchValueClearing, setFilteredClearingAccounts]);
useTitle(`${group.name} - Accounts`);
const openAccountEdit = (account) => {
setAccountToEdit(account);
setShowPersonalAccountEditModal(true);
};
const closeAccountEdit = (evt, reason) => {
if (reason !== "backdropClick") {
setShowPersonalAccountEditModal(false);
setAccountToEdit(null);
}
};
const openClearingAccountEdit = (account) => {
setClearingAccountToEdit(account);
setShowClearingAccountEditModal(true);
};
const closeClearingAccountEdit = (evt, reason) => {
if (reason !== "backdropClick") {
setShowClearingAccountEditModal(false);
setClearingAccountToEdit(null);
}
};
const confirmDeleteAccount = () => {
if (accountToDelete !== null) {
deleteAccount({ accountID: accountToDelete })
.then((account) => {
updateAccount(account, setAccounts);
setAccountToDelete(null);
})
.catch((err) => {
toast.error(err);
});
}
};
const openCreateDialog = () => {
setClearingAccountToCopy(undefined);
setShowClearingAccountCreationModal(true);
};
const copyClearingAccount = (account) => {
setClearingAccountToCopy(account);
setShowClearingAccountCreationModal(true);
};
return (
<>
<MobilePaper>
<TabContext value={activeTab}>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<TabList onChange={(e, newValue) => setActiveTab(newValue)} centered>
<Tab
value="personal"
label={
<TextBadge badgeContent={personalAccounts.length} color="primary">
<span>Personal Accounts</span>
</TextBadge>
}
/>
<Tab
label={
<TextBadge badgeContent={clearingAccounts.length} color="primary">
<span>Clearing Accounts</span>
</TextBadge>
}
value="clearing"
/>
</TabList>
</Box>
<TabPanel value="personal">
<List>
{personalAccounts.length === 0 ? (
<Alert severity="info">No Accounts</Alert>
) : (
<>
<ListItem>
<Input
value={searchValuePersonal}
onChange={(e) => setSearchValuePersonal(e.target.value)}
placeholder="Search…"
inputProps={{
"aria-label": "search",
}}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="clear search input"
onClick={(e) => setSearchValuePersonal("")}
edge="end"
>
<Clear />
</IconButton>
</InputAdornment>
}
/>
</ListItem>
<Divider />
{filteredPersonalAccounts.map((account) => (
<ListItem sx={{ padding: 0 }} key={account.id}>
<ListItemLink to={`/groups/${group.id}/accounts/${account.id}`}>
<ListItemText
primary={
<div>
<span>{account.name}</span>
{account.owning_user_id === currentUser.id ? (
<span>
, owned by{" "}
<Chip
size="small"
component="span"
color="primary"
label="you"
/>
</span>
) : (
account.owning_user_id !== null && (
<span>
, owned by{" "}
<Chip
size="small"
component="span"
color="secondary"
label={
memberIDToUsername[
account.owning_user_id
]
}
/>
</span>
)
)}
</div>
}
secondary={account.description}
/>
</ListItemLink>
{userPermissions.can_write && (
<ListItemSecondaryAction>
<IconButton
color="primary"
onClick={() => openAccountEdit(account)}
>
<Edit />
</IconButton>
<IconButton
color="error"
onClick={() => setAccountToDelete(account.id)}
>
<Delete />
</IconButton>
</ListItemSecondaryAction>
)}
</ListItem>
))}
</>
)}
</List>
{userPermissions.can_write && (
<>
<Grid container justifyContent="center">
<Tooltip title="Create Personal Account">
<IconButton
color="primary"
onClick={() => setShowPersonalAccountCreationModal(true)}
>
<Add />
</IconButton>
</Tooltip>
</Grid>
<CreateAccountModal
show={showPersonalAccountCreationModal}
onClose={(evt, reason) => {
if (reason !== "backdropClick") {
setShowPersonalAccountCreationModal(false);
}
}}
group={group}
/>
<EditAccountModal
show={showPersonalAccountEditModal}
onClose={closeAccountEdit}
account={accountToEdit}
group={group}
/>
</>
)}
</TabPanel>
<TabPanel value="clearing">
<List>
{clearingAccounts.length === 0 ? (
<Alert severity="info">No Accounts</Alert>
) : (
<>
<ListItem>
<Input
value={searchValueClearing}
onChange={(e) => setSearchValueClearing(e.target.value)}
placeholder="Search…"
inputProps={{
"aria-label": "search",
}}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="clear search input"
onClick={(e) => setSearchValueClearing("")}
edge="end"
>
<Clear />
</IconButton>
</InputAdornment>
}
/>
</ListItem>
<Divider />
{filteredClearingAccounts.map((account) => (
<ListItem sx={{ padding: 0 }} key={account.id}>
<ListItemLink to={`/groups/${group.id}/accounts/${account.id}`}>
<ListItemText primary={account.name} secondary={account.description} />
</ListItemLink>
{userPermissions.can_write && (
<ListItemSecondaryAction>
<IconButton
color="primary"
onClick={() => openClearingAccountEdit(account)}
>
<Edit />
</IconButton>
<IconButton
color="primary"
onClick={() => copyClearingAccount(account)}
>
<ContentCopy />
</IconButton>
<IconButton
color="error"
onClick={() => setAccountToDelete(account.id)}
>
<Delete />
</IconButton>
</ListItemSecondaryAction>
)}
</ListItem>
))}
</>
)}
</List>
{userPermissions.can_write && (
<>
<Grid container justifyContent="center">
<Tooltip title="Create Clearing Account">
<IconButton color="primary" onClick={openCreateDialog}>
<Add />
</IconButton>
</Tooltip>
</Grid>
<CreateClearingAccountModal
show={showClearingAccountCreationModal}
onClose={(evt, reason) => {
if (reason !== "backdropClick") {
setShowClearingAccountCreationModal(false);
}
}}
initialValues={clearingAccountToCopy}
group={group}
/>
<EditClearingAccountModal
show={showClearingAccountEditModal}
onClose={closeClearingAccountEdit}
account={clearingAccountToEdit}
group={group}
/>
</>
)}
</TabPanel>
</TabContext>
</MobilePaper>
{userPermissions.can_write && (
<>
<SpeedDial
ariaLabel="Create Account"
sx={{ position: "fixed", bottom: 20, right: 20 }}
icon={<SpeedDialIcon />}
// onClose={() => setSpeedDialOpen(false)}
// onOpen={() => setSpeedDialOpen(true)}
onClick={toggleSpeedDial}
open={speedDialOpen}
>
<SpeedDialAction
icon={<PersonalAccountIcon />}
tooltipTitle="Personal"
tooltipOpen
onClick={() => setShowPersonalAccountCreationModal(true)}
/>
<SpeedDialAction
icon={<ClearingAccountIcon />}
tooltipTitle="Clearing"
tooltipOpen
onClick={openCreateDialog}
/>
</SpeedDial>
<Dialog maxWidth="xs" aria-labelledby="confirmation-dialog-title" open={accountToDelete !== null}>
<DialogTitle id="confirmation-dialog-title">Confirm delete account</DialogTitle>
<DialogContent dividers>
Are you sure you want to delete the account "
{allAccounts.find((acc) => acc.id === accountToDelete)?.name}"
</DialogContent>
<DialogActions>
<Button autoFocus onClick={() => setAccountToDelete(null)} color="primary">
Cancel
</Button>
<Button onClick={confirmDeleteAccount} color="error">
Ok
</Button>
</DialogActions>
</Dialog>
</>
)}
</>
);
}
Example #4
Source File: GroupMemberList.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function GroupMemberList({ group }) {
const [showEditMemberDialog, setShowEditMemberDialog] = useState(false);
const [memberToEdit, setMemberToEdit] = useState(null);
const currentUser = useRecoilValue(userData);
const members = useRecoilValue(groupMembers(group.id));
const permissions = useRecoilValue(currUserPermissions(group.id));
useTitle(`${group.name} - Members`);
const handleEditMemberSubmit = (values, { setSubmitting }) => {
updateGroupMemberPrivileges({
groupID: group.id,
userID: values.userID,
canWrite: values.canWrite,
isOwner: values.isOwner,
})
.then((result) => {
setSubmitting(false);
setShowEditMemberDialog(false);
toast.success("Successfully updated group member permissions");
})
.catch((err) => {
setSubmitting(false);
toast.error(err);
});
};
const getMemberUsername = (member_id) => {
const member = members.find((member) => member.user_id === member_id);
if (member === undefined) {
return "unknown";
}
return member.username;
};
const closeEditMemberModal = () => {
setShowEditMemberDialog(false);
setMemberToEdit(null);
};
const openEditMemberModal = (userID) => {
const user = members.find((member) => member.user_id === userID);
// TODO: maybe deal with disappearing users in the list
setMemberToEdit(user);
setShowEditMemberDialog(true);
};
return (
<MobilePaper>
<List>
{members.length === 0 ? (
<ListItem>
<ListItemText primary="No Members" />
</ListItem>
) : (
members.map((member, index) => (
<ListItem key={index}>
<ListItemText
primary={
<>
<span style={{ marginRight: 5 }}>{member.username}</span>
{member.is_owner ? (
<Chip
size="small"
sx={{ mr: 1 }}
component="span"
color="primary"
label="owner"
variant="outlined"
/>
) : member.can_write ? (
<Chip
size="small"
sx={{ mr: 1 }}
component="span"
color="primary"
label="editor"
variant="outlined"
/>
) : null}
{member.user_id === currentUser.id ? (
<Chip
size="small"
sx={{ mr: 1 }}
component="span"
color="primary"
label="it's you"
/>
) : (
""
)}
</>
}
secondary={
<>
{member.invited_by && (
<small className="text-muted">
invited by {getMemberUsername(member.invited_by)}
</small>
)}
<small className="text-muted">
joined{" "}
{DateTime.fromISO(member.joined_at).toLocaleString(DateTime.DATETIME_FULL)}
</small>
</>
}
/>
{permissions.is_owner || permissions.can_write ? (
<ListItemSecondaryAction>
<IconButton onClick={() => openEditMemberModal(member.user_id)}>
<Edit />
</IconButton>
</ListItemSecondaryAction>
) : (
""
)}
</ListItem>
))
)}
</List>
<Dialog open={showEditMemberDialog} onClose={closeEditMemberModal}>
<DialogTitle>Edit Group Member</DialogTitle>
<DialogContent>
<Formik
initialValues={{
userID: memberToEdit ? memberToEdit.user_id : -1,
isOwner: memberToEdit ? memberToEdit.is_owner : false,
canWrite: memberToEdit ? memberToEdit.can_write : false,
}}
onSubmit={handleEditMemberSubmit}
enableReinitialize={true}
>
{({ values, handleBlur, handleChange, handleSubmit, isSubmitting }) => (
<Form>
<FormControlLabel
control={
<Checkbox
name="canWrite"
onBlur={handleBlur}
onChange={handleChange}
checked={values.canWrite}
/>
}
label="Can Write"
/>
<FormControlLabel
control={
<Checkbox
name="isOwner"
onBlur={handleBlur}
onChange={handleChange}
checked={values.isOwner}
/>
}
label="Is Owner"
/>
{isSubmitting && <LinearProgress />}
<DialogActions>
<Button type="submit" color="primary">
Save
</Button>
<Button color="error" onClick={closeEditMemberModal}>
Close
</Button>
</DialogActions>
</Form>
)}
</Formik>
</DialogContent>
</Dialog>
</MobilePaper>
);
}
Example #5
Source File: GroupSettings.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function GroupSettings({ group }) {
const [showLeaveModal, setShowLeaveModal] = useState(false);
const history = useHistory();
const userPermissions = useRecoilValue(currUserPermissions(group.id));
const [isEditing, setIsEditing] = useState(false);
useTitle(`${group.name} - Settings`);
const startEdit = () => {
setIsEditing(true);
};
const stopEdit = () => {
setIsEditing(false);
};
const handleSubmit = (values, { setSubmitting }) => {
updateGroupMetadata({
groupID: group.id,
name: values.name,
description: values.description,
currencySymbol: values.currencySymbol,
terms: values.terms,
addUserAccountOnJoin: values.addUserAccountOnJoin,
})
.then((res) => {
setSubmitting(false);
setIsEditing(false);
})
.catch((err) => {
setSubmitting(false);
toast.error(err);
});
};
const confirmLeaveGroup = () => {
leaveGroup({ groupID: group.id })
.then((res) => {
history.push("/");
})
.catch((err) => {
toast.error(err);
});
};
return (
<MobilePaper>
{userPermissions.is_owner ? (
<Alert severity="info">You are an owner of this group</Alert>
) : !userPermissions.can_write ? (
<Alert severity="info">You only have read access to this group</Alert>
) : null}
<Formik
initialValues={{
name: group.name,
description: group.description,
terms: group.terms,
currencySymbol: group.currency_symbol,
addUserAccountOnJoin: group.add_user_account_on_join,
}}
onSubmit={handleSubmit}
validationSchema={validationSchema}
enableReinitialize={true}
>
{({ values, handleBlur, handleChange, handleSubmit, isSubmitting }) => (
<Form onSubmit={handleSubmit}>
<DisabledTextField
variant="standard"
margin="normal"
required
fullWidth
type="text"
label="Name"
name="name"
disabled={!userPermissions.can_write || !isEditing}
onBlur={handleBlur}
onChange={handleChange}
value={values.name}
/>
<DisabledTextField
variant="standard"
margin="normal"
fullWidth
type="text"
name="description"
label="Description"
disabled={!userPermissions.can_write || !isEditing}
onBlur={handleBlur}
onChange={handleChange}
value={values.description}
/>
<DisabledTextField
variant="standard"
margin="normal"
required
fullWidth
type="text"
name="currencySymbol"
label="Currency"
disabled={!userPermissions.can_write || !isEditing}
onBlur={handleBlur}
onChange={handleChange}
value={values.currencySymbol}
/>
<DisabledTextField
variant="standard"
multiline={true}
margin="normal"
fullWidth
type="text"
name="terms"
label="Terms"
disabled={!userPermissions.can_write || !isEditing}
onBlur={handleBlur}
onChange={handleChange}
value={values.terms}
/>
<FormGroup>
<DisabledFormControlLabel
control={
<Checkbox
name="addUserAccountOnJoin"
disabled={!userPermissions.can_write || !isEditing}
onBlur={handleBlur}
onChange={handleChange}
checked={values.addUserAccountOnJoin}
/>
}
label="Automatically add accounts for newly joined group members"
/>
</FormGroup>
{isSubmitting && <LinearProgress />}
<Grid container justifyContent="space-between" style={{ marginTop: 10 }}>
<div>
{userPermissions.can_write && isEditing && (
<>
<Button
type="submit"
variant="contained"
color="primary"
disabled={isSubmitting}
startIcon={<Save />}
>
Save
</Button>
<Button
variant="contained"
color="error"
disabled={isSubmitting}
onClick={stopEdit}
startIcon={<Cancel />}
sx={{ ml: 1 }}
>
Cancel
</Button>
</>
)}
{userPermissions.can_write && !isEditing && (
<Button
variant="contained"
color="primary"
disabled={isSubmitting}
onClick={startEdit}
startIcon={<Edit />}
>
Edit
</Button>
)}
</div>
<Button variant="contained" onClick={() => setShowLeaveModal(true)}>
Leave Group
</Button>
</Grid>
</Form>
)}
</Formik>
{/*<List>*/}
{/* <ListItem>*/}
{/* <ListItemText primary="Created" secondary={group.created}/>*/}
{/* </ListItem>*/}
{/* <ListItem>*/}
{/* <ListItemText primary="Joined" secondary={group.joined}/>*/}
{/* </ListItem>*/}
{/*</List>*/}
<Dialog open={showLeaveModal} onClose={() => setShowLeaveModal(false)}>
<DialogTitle>Leave Group</DialogTitle>
<DialogContent>
<DialogContentText>
<span>
Are you sure you want to leave the group {group.name}. If you are the last member to leave
this group it will be deleted and its transaction will be lost forever...
</span>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="secondary" onClick={confirmLeaveGroup}>
Yes pls
</Button>
<Button color="primary" onClick={() => setShowLeaveModal(false)}>
No
</Button>
</DialogActions>
</Dialog>
</MobilePaper>
);
}
Example #6
Source File: SessionList.tsx From abrechnung with GNU Affero General Public License v3.0 | 4 votes |
export default function SessionList() {
// TODO: fix editing functions
const [editedSessions, setEditedSessions] = useState({});
const [sessionToDelete, setSessionToDelete] = useState({
show: false,
toDelete: null,
});
const user = useRecoilValue(userData);
const sessions = user.sessions;
useTitle("Abrechnung - Sessions");
const editSession = (id) => {
if (!editedSessions.hasOwnProperty(id)) {
const newSessions = {
...editedSessions,
[id]: sessions.find((session) => session.id === id)?.name,
};
setEditedSessions(newSessions);
}
};
const stopEditSession = (id) => {
if (editedSessions.hasOwnProperty(id)) {
let newEditedSessions = { ...editedSessions };
delete newEditedSessions[id];
setEditedSessions(newEditedSessions);
}
};
const closeDeleteSessionModal = () => {
setSessionToDelete({ show: false, toDelete: null });
};
const performRename = (id) => {
if (editedSessions.hasOwnProperty(id)) {
renameSession({
sessionID: id,
name: editedSessions[id],
}).catch((err) => {
toast.error(err);
});
stopEditSession(id);
}
};
const openDeleteSessionModal = (id) => {
setSessionToDelete({ show: true, toDelete: id });
};
const confirmDeleteSession = () => {
if (sessionToDelete.toDelete !== null) {
deleteSession({ sessionID: sessionToDelete.toDelete }).catch((err) => {
toast.error(err);
});
setSessionToDelete({ show: false, toDelete: null });
}
};
const handleEditChange = (id, value) => {
const newEditedSessions = { ...editedSessions, [id]: value };
setEditedSessions(newEditedSessions);
};
const onKeyUp = (id) => (key) => {
if (key.keyCode === 13) {
performRename(id);
}
};
return (
<MobilePaper>
<Typography component="h3" variant="h5">
Login Sessions
</Typography>
<List>
{sessions.map((session) => {
if (editedSessions.hasOwnProperty(session.id)) {
return (
<ListItem key={session.id}>
<TextField
margin="normal"
variant="standard"
fullWidth
onKeyUp={onKeyUp(session.id)}
value={editedSessions[session.id]}
onChange={(event) => handleEditChange(session.id, event.target.value)}
/>
<ListItemSecondaryAction>
<Button onClick={() => performRename(session.id)}>
<Check />
</Button>
<Button onClick={() => stopEditSession(session.id)}>
<Close />
</Button>
</ListItemSecondaryAction>
</ListItem>
);
} else {
return (
<ListItem key={session.id}>
<ListItemText
primary={session.name}
secondary={
<>
<span>
Valid until{" "}
{DateTime.fromISO(session.valid_until).toLocaleString(
DateTime.DATETIME_FULL
) && "indefinitely"}
,{" "}
</span>
<span>
Last seen on{" "}
{DateTime.fromISO(session.last_seen).toLocaleString(
DateTime.DATETIME_FULL
)}
</span>
</>
}
/>
<ListItemSecondaryAction>
<IconButton onClick={() => editSession(session.id)}>
<Edit />
</IconButton>
<IconButton onClick={() => openDeleteSessionModal(session.id)}>
<Delete />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
);
}
})}
</List>
<Dialog open={sessionToDelete.show} onClose={closeDeleteSessionModal}>
<DialogTitle>Delete Session?</DialogTitle>
<DialogContent>
<DialogContentText>
{sessionToDelete.toDelete !== null
? `Are you sure you want to delete session ${
sessions.find((session) => session.id === sessionToDelete.toDelete)?.name
}`
: null}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button color="secondary" onClick={confirmDeleteSession}>
Yes pls
</Button>
<Button color="primary" onClick={closeDeleteSessionModal}>
No
</Button>
</DialogActions>
</Dialog>
</MobilePaper>
);
}
Example #7
Source File: Config.tsx From NekoMaid with MIT License | 4 votes |
configs.push({
title: lang.config.serverConfig,
component () {
const plugin = usePlugin()
const globalData = useGlobalData()
const [flag, update] = useState(0)
const [info, setInfo] = useState<Record<string, string>>({ })
const [open, setOpen] = useState(false)
const [canGetData, setCanGetData] = useState(true)
const [loading, setLoading] = useState(false)
const setValue = (field: string, value: any, isGlobal = true) => {
plugin.emit('server:set', field, value)
success()
if (isGlobal) {
(globalData as any)[field] = value
update(flag + 1)
location.reload()
}
}
const createEditButtom = (field: string, isGlobal?: boolean, isInt = true) => <IconButton
onClick={() => dialog(
{
content: lang.inputValue,
input: isInt
? {
error: true,
type: 'number',
helperText: lang.invalidValue,
validator: (it: string) => /^\d+$/.test(it) && +it >= 0
}
: { }
}).then(res => res != null && setValue(field, isInt ? parseInt(res as any) : (res || null), isGlobal))}
><Edit /></IconButton>
const infoElm: JSX.Element[] = []
for (const key in info) {
const name = (lang.config as any)[key]
infoElm.push(<ListItem key={key} sx={{ pl: 4 }}>
<ListItemText
primary={key === 'isAikarFlags' ? <Link href='https://mcflags.emc.gs' target='_blank' rel='noopener'>{name}</Link> : name}
secondary={info[key].toString()}
/>
</ListItem>)
}
return <List>
<CircularLoading loading={loading} />
<ListItem secondaryAction={globalData.canSetMaxPlayers
? createEditButtom('maxPlayers')
: undefined}>
<ListItemText primary={lang.config.maxPlayers + ': ' + globalData.maxPlayers} />
</ListItem>
<ListItem secondaryAction={createEditButtom('spawnRadius')}>
<ListItemText primary={lang.config.spawnRadius + ': ' + globalData.spawnRadius} />
</ListItem>
<ListItem secondaryAction={createEditButtom('motd', false, false)}>
<ListItemText primary={lang.config.motd} />
</ListItem>
<ListItem secondaryAction={<Switch checked={globalData.hasWhitelist} onChange={e => setValue('hasWhitelist', e.target.checked)} />}>
<ListItemText primary={lang.config.whitelist} />
</ListItem>
{canGetData && <>
<ListItemButton onClick={() => {
if (infoElm.length) setOpen(!open)
else {
setLoading(true)
plugin.emit('server:fetchInfo', (data: any) => {
setLoading(false)
if (!data) {
failed(lang.unsupported)
setCanGetData(false)
return
}
setInfo(data)
setOpen(true)
})
}
}}>
<ListItemIcon><Equalizer /></ListItemIcon>
<ListItemText primary={lang.info} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout='auto' unmountOnExit>
<List component='div' dense disablePadding>{infoElm}</List>
</Collapse>
</>}
</List>
}
},
{
title: lang.history,
component () {
const [cur, update] = useState(0)
const list: ServerRecord[] = JSON.parse(localStorage.getItem('NekoMaid:servers') || '[]')
return <List>
{list.sort((a, b) => b.time - a.time).map(it => {
const i = it.address.indexOf('?')
return <ListItem
disablePadding
key={it.address}
secondaryAction={<IconButton edge='end' size='small' onClick={() => {
localStorage.setItem('NekoMaid:servers', JSON.stringify(list.filter(s => s.address !== it.address)))
success()
update(cur + 1)
}}><Delete /></IconButton>}
>
<ListItemButton onClick={() => {
location.hash = ''
location.search = it.address
}} dense>
<ListItemAvatar><Avatar src={it.icon} variant='rounded'><HelpOutline /></Avatar></ListItemAvatar>
<ListItemText primary={<Tooltip title={it.address.slice(i + 1)}>
<span>{it.address.slice(0, i)}</span></Tooltip>} secondary={dayjs(it.time).fromNow()} />
</ListItemButton>
</ListItem>
})}
</List>
}
},
{
title: lang.config.theme,
component () {
const color = localStorage.getItem('NekoMaid:color') || 'blue'
return <CardContent sx={{ textAlign: 'center' }}>
<Box>
<ToggleButtonGroup exclusive value={localStorage.getItem('NekoMaid:colorMode') || ''} onChange={(_, it) => {
localStorage.setItem('NekoMaid:colorMode', it)
location.reload()
}}>
<ToggleButton value='light'><Brightness7 /> {lang.config.light}</ToggleButton>
<ToggleButton value=''><SettingsBrightness /> {lang.config.system}</ToggleButton>
<ToggleButton value='dark'><Brightness4 /> {lang.config.dark}</ToggleButton>
</ToggleButtonGroup>
</Box>
<Paper sx={{ marginTop: 2, width: '176px', overflow: 'hidden', display: 'inline-block' }}>
{Object.keys(colors).slice(1, 17).map((key, i) => {
const checked = color === key
const elm = <Box
key={key}
onClick={() => {
localStorage.setItem('NekoMaid:color', key)
location.reload()
}}
sx={{
backgroundColor: (colors as any)[key][600],
width: '44px',
height: '44px',
display: 'inline-block',
cursor: 'pointer'
}}
><Check htmlColor='white' sx={{ top: '10px', position: 'relative', opacity: checked ? 1 : 0 }} /></Box>
return (i + 1) % 4 === 0 ? <React.Fragment key={key}>{elm}<br /></React.Fragment> : elm
})}
</Paper>
</CardContent>
}
})
Example #8
Source File: Worlds.tsx From NekoMaid with MIT License | 4 votes |
Worlds: React.FC = () => {
const plugin = usePlugin()
const globalData = useGlobalData()
const [worlds, setWorlds] = useState<World[]>([])
const [selected, setSelected] = useState('')
const [open, setOpen] = useState(false)
const update = () => plugin.emit('worlds:fetch', (data: World[]) => {
setWorlds(data)
if (data.length) setSelected(old => data.some(it => it.id === old) ? old : '')
})
useEffect(() => {
const offUpdate = plugin.on('worlds:update', update)
update()
return () => { offUpdate() }
}, [])
const sw = worlds.find(it => it.id === selected)
const getSwitch = (name: string, configId = name) => sw
? <ListItem
secondaryAction={<Switch disabled={!globalData.hasMultiverse} checked={(sw as any)[name]}
onChange={e => {
plugin.emit('worlds:set', sw.id, configId, e.target.checked.toString())
success()
}}
/>}><ListItemText primary={(lang.worlds as any)[name]} /></ListItem>
: null
return <Box sx={{ minHeight: '100%', py: 3 }}>
<Toolbar />
<Container maxWidth={false}>
<Grid container spacing={3}>
<Grid item lg={8} md={12} xl={9} xs={12}>
<Card>
<CardHeader title={lang.worlds.title} />
<Divider />
<Box sx={{ position: 'relative' }}>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell padding='checkbox' />
<TableCell>{lang.worlds.name}</TableCell>
{globalData.hasMultiverse && <TableCell>{lang.worlds.alias}</TableCell>}
<TableCell>{lang.worlds.players}</TableCell>
<TableCell>{lang.worlds.chunks}</TableCell>
<TableCell>{lang.worlds.entities}</TableCell>
<TableCell>{lang.worlds.tiles}</TableCell>
<TableCell>{lang.worlds.time}</TableCell>
<TableCell>{lang.worlds.weather}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{worlds.map(it => <TableRow key={it.id}>
<TableCell padding='checkbox'><Checkbox checked={selected === it.id} onClick={() => setSelected(it.id)} /></TableCell>
<TableCell><Tooltip title={it.id}><span>{it.name}</span></Tooltip></TableCell>
{globalData.hasMultiverse && <TableCell>{it.alias}
<IconButton size='small' onClick={() => dialog(lang.inputValue, lang.worlds.alias).then(res => {
if (res == null) return
plugin.emit('worlds:set', it.id, 'alias', res)
success()
})}><Edit fontSize='small' /></IconButton>
</TableCell>}
<TableCell>{it.players}</TableCell>
<TableCell>{it.chunks}</TableCell>
<TableCell>{it.entities}</TableCell>
<TableCell>{it.tiles}</TableCell>
<TableCell><Countdown time={it.time} max={24000} interval={50} /></TableCell>
<TableCell><IconButton size='small' onClick={() => {
plugin.emit('worlds:weather', it.id)
success()
}}>
{React.createElement((it.weather === 1 ? WeatherRainy : it.weather === 2 ? WeatherLightningRainy : WbSunny) as any)}
</IconButton></TableCell>
</TableRow>)}
</TableBody>
</Table>
</TableContainer>
</Box>
</Card>
</Grid>
<Grid item lg={4} md={6} xl={3} xs={12}>
<Card>
<CardHeader
title={lang.operations}
sx={{ position: 'relative' }}
action={<Tooltip title={lang.worlds.save} placement='left'>
<IconButton
size='small'
onClick={() => {
if (!sw) return
plugin.emit('worlds:save', sw.id)
success()
}}
sx={cardActionStyles}
><Save /></IconButton>
</Tooltip>}
/>
<Divider />
<Box sx={{ position: 'relative' }}>
{sw
? <List sx={{ width: '100%' }} component='nav'>
<ListItem secondaryAction={<ToggleButtonGroup
exclusive
color='primary'
size='small'
value={sw.difficulty}
onChange={(_, value) => {
plugin.emit('worlds:difficulty', sw.id, value)
success()
}}
>
{difficulties.map(it => <ToggleButton value={it.toUpperCase()} key={it}>{minecraft['options.difficulty.' + it]}</ToggleButton>)}
</ToggleButtonGroup>}><ListItemText primary={minecraft['options.difficulty']} /></ListItem>
<ListItem secondaryAction={<Switch checked={sw.pvp} onChange={e => {
plugin.emit('worlds:pvp', sw.id, e.target.checked)
success()
}} />}><ListItemText primary='PVP' /></ListItem>
{getSwitch('allowAnimals', 'spawning.animals.spawn')}
{getSwitch('allowMonsters', 'spawning.monsters.spawn')}
{globalData.hasMultiverse && <>
{getSwitch('allowFlight')}
{getSwitch('autoHeal')}
{getSwitch('hunger')}
</>}
<ListItem secondaryAction={globalData.canSetViewDistance
? <IconButton
onClick={() => dialog({
content: lang.inputValue,
input: {
error: true,
type: 'number',
helperText: lang.invalidValue,
validator: (it: string) => /^\d+$/.test(it) && +it > 1 && +it < 33
}
}).then(res => {
if (!res) return
plugin.emit('worlds:viewDistance', sw.id, parseInt(res as any))
success()
})}
><Edit /></IconButton>
: undefined}>
<ListItemText primary={lang.worlds.viewDistance + ': ' + sw.viewDistance} />
</ListItem>
<ListItem><ListItemText primary={minecraft['selectWorld.enterSeed']} secondary={sw.seed} /></ListItem>
<ListItemButton onClick={() => setOpen(!open)}>
<ListItemText primary={minecraft['selectWorld.gameRules']} />
{open ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open} timeout="auto" unmountOnExit>
<List component='div' dense disablePadding>
{sw.rules.map(([key, value]) => {
const isTrue = value === 'true'
const isBoolean = isTrue || value === 'false'
const isNumber = /^\d+$/.test(value)
return <ListItem
key={key}
sx={{ pl: 4 }}
secondaryAction={isBoolean
? <Switch
checked={isTrue}
onChange={e => {
plugin.emit('worlds:rule', sw.id, key, e.target.checked.toString())
success()
}}
/>
: <IconButton
onClick={() => dialog({
content: lang.inputValue,
input: isNumber
? {
error: true,
type: 'number',
helperText: lang.invalidValue,
validator: (it: string) => /^\d+$/.test(it)
}
: { }
}).then(res => {
if (res == null) return
plugin.emit('worlds:rule', sw.id, key, res)
success()
})}
><Edit /></IconButton>}
>
<ListItemText primary={(minecraft['gamerule.' + key] || key) + (isBoolean ? '' : ': ' + value)} />
</ListItem>
})}
</List>
</Collapse>
</List>
: <CardContent><Empty /></CardContent>
}
</Box>
</Card>
</Grid>
</Grid>
</Container>
</Box>
}