added all fields to group in collapsable
This commit is contained in:
parent
a6045a8ba6
commit
34f38e90d5
9
package-lock.json
generated
9
package-lock.json
generated
@ -10,6 +10,7 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@solidjs/router": "^0.13.5",
|
||||
"solid-collapse": "^1.1.0",
|
||||
"solid-icons": "^1.1.0",
|
||||
"solid-js": "^1.8.11"
|
||||
},
|
||||
@ -1750,6 +1751,14 @@
|
||||
"seroval": "^1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/solid-collapse": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/solid-collapse/-/solid-collapse-1.1.0.tgz",
|
||||
"integrity": "sha512-QAnJnpJ2gCfQaq6jpXUD76talS80upxcSCM9DTeymob+WlBgVFK6nPxsDqjd1dgSiVmXtkO05EO+hTjoOEXfXw==",
|
||||
"peerDependencies": {
|
||||
"solid-js": ">=1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/solid-devtools": {
|
||||
"version": "0.29.3",
|
||||
"resolved": "https://registry.npmjs.org/solid-devtools/-/solid-devtools-0.29.3.tgz",
|
||||
|
@ -16,6 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@solidjs/router": "^0.13.5",
|
||||
"solid-collapse": "^1.1.0",
|
||||
"solid-icons": "^1.1.0",
|
||||
"solid-js": "^1.8.11"
|
||||
}
|
||||
|
@ -60,7 +60,6 @@ const AppLayout: Component<RouteSectionProps> = (props) => {
|
||||
<header class={styles.header}>
|
||||
<div></div>
|
||||
<nav class={styles.navigation}>
|
||||
<A href='/wishlists'><BsBookmarkHeartFill class={styles.icon} />Wishlists</A>
|
||||
<A href='/groups'><BsPeopleFill class={styles.icon} /> Groups</A>
|
||||
</nav>
|
||||
<div class={styles.loggedIn}>
|
||||
|
17
src/api.ts
17
src/api.ts
@ -23,6 +23,9 @@ export interface Group {
|
||||
owner: User,
|
||||
members: User[],
|
||||
strats: Strat[],
|
||||
maps: Map[],
|
||||
profiles: Profile[],
|
||||
stratTypes: string[],
|
||||
}
|
||||
|
||||
export interface Strat {
|
||||
@ -180,6 +183,18 @@ export class API {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
public async removeGroup(groupId: string): Promise<void> {
|
||||
const response = await fetch(this.apiURL + '/group/' + groupId, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + this.ensureLoggedIn().token
|
||||
},
|
||||
});
|
||||
|
||||
await this.checkResponse(response);
|
||||
}
|
||||
|
||||
public async updateGroup(id: string, group: GroupRequest): Promise<Group> {
|
||||
const response = await fetch(this.apiURL + '/group/' + id, {
|
||||
method: 'PUT',
|
||||
@ -336,7 +351,7 @@ export class API {
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
public async removeProfileToGroup(groupId: string, profileId: string): Promise<void> {
|
||||
public async removeProfileFromGroup(groupId: string, profileId: string): Promise<void> {
|
||||
const response = await fetch(this.apiURL + '/group/' + groupId + '/profile/' + profileId, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
|
0
src/components/MapItem.module.css
Normal file
0
src/components/MapItem.module.css
Normal file
55
src/components/MapItem.tsx
Normal file
55
src/components/MapItem.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import styles from './MapItem.module.css';
|
||||
import { Component } from "solid-js";
|
||||
import { Group, Map, api } from "../api";
|
||||
import { showDialog, showMessageDialog } from "../state";
|
||||
import { errorToString } from "../util";
|
||||
import { BsTrash } from 'solid-icons/bs';
|
||||
|
||||
export interface MapItemProps {
|
||||
group: Group,
|
||||
map: Map,
|
||||
onDelete: (m: Map) => void
|
||||
}
|
||||
|
||||
const MapItem: Component<MapItemProps> = (props) => {
|
||||
|
||||
const deleteMap = async () => {
|
||||
showDialog({
|
||||
title: 'Remove map',
|
||||
text: 'Do you want to remove this map?',
|
||||
buttons: [
|
||||
{
|
||||
name: 'Remove',
|
||||
type: 'danger',
|
||||
action: async () => {
|
||||
try {
|
||||
await api().removeMap(props.group.id, props.map.id);
|
||||
props.onDelete(props.map);
|
||||
} catch (e) {
|
||||
showMessageDialog('Failed to remove map', errorToString(e));
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Cancel',
|
||||
action: () => { }
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={styles.map}>
|
||||
<div class={styles.mapDetails}>
|
||||
<div class={styles.mapName}>
|
||||
<span innerText={props.map.name} />
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={deleteMap}><BsTrash /></button>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
export default MapItem;
|
0
src/components/MemberItem.module.css
Normal file
0
src/components/MemberItem.module.css
Normal file
55
src/components/MemberItem.tsx
Normal file
55
src/components/MemberItem.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import styles from './MemberItem.module.css';
|
||||
import { Component } from "solid-js";
|
||||
import { Group, User, api } from "../api";
|
||||
import { showDialog, showMessageDialog } from "../state";
|
||||
import { errorToString } from "../util";
|
||||
import { BsTrash } from 'solid-icons/bs';
|
||||
|
||||
export interface MemberItemProps {
|
||||
group: Group,
|
||||
member: User,
|
||||
onDelete: (m: User) => void
|
||||
}
|
||||
|
||||
const MemberItem: Component<MemberItemProps> = (props) => {
|
||||
|
||||
const deleteMember = async () => {
|
||||
showDialog({
|
||||
title: 'Remove member',
|
||||
text: 'Do you want to remove this member?',
|
||||
buttons: [
|
||||
{
|
||||
name: 'Remove',
|
||||
type: 'danger',
|
||||
action: async () => {
|
||||
try {
|
||||
await api().removeMember(props.group.id, props.member.username);
|
||||
props.onDelete(props.member);
|
||||
} catch (e) {
|
||||
showMessageDialog('Failed to remove member', errorToString(e));
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Cancel',
|
||||
action: () => { }
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={styles.member}>
|
||||
<div class={styles.memberDetails}>
|
||||
<div class={styles.memberName}>
|
||||
<span innerText={props.member.displayName} />
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={deleteMember}><BsTrash /></button>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
export default MemberItem;
|
0
src/components/ProfileItem.module.css
Normal file
0
src/components/ProfileItem.module.css
Normal file
55
src/components/ProfileItem.tsx
Normal file
55
src/components/ProfileItem.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import styles from './ProfileItem.module.css';
|
||||
import { Component } from "solid-js";
|
||||
import { showDialog, showMessageDialog } from "../state";
|
||||
import { errorToString } from "../util";
|
||||
import { BsTrash } from 'solid-icons/bs';
|
||||
import { Group, Profile, api } from '../api';
|
||||
|
||||
export interface ProfileItemProps {
|
||||
group: Group,
|
||||
profile: Profile,
|
||||
onDelete: (p: Profile) => void
|
||||
}
|
||||
|
||||
const ProfileItem: Component<ProfileItemProps> = (props) => {
|
||||
|
||||
const deleteProfile = async () => {
|
||||
showDialog({
|
||||
title: 'Remove profile',
|
||||
text: 'Do you want to remove this profile?',
|
||||
buttons: [
|
||||
{
|
||||
name: 'Remove',
|
||||
type: 'danger',
|
||||
action: async () => {
|
||||
try {
|
||||
await api().removeProfileFromGroup(props.group.id, props.profile.id);
|
||||
props.onDelete(props.profile);
|
||||
} catch (e) {
|
||||
showMessageDialog('Failed to remove profile', errorToString(e));
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Cancel',
|
||||
action: () => { }
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={styles.profile}>
|
||||
<div class={styles.profileDetails}>
|
||||
<div class={styles.profileName}>
|
||||
<span innerText={props.profile.name} />
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={deleteProfile}><BsTrash /></button>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
export default ProfileItem;
|
0
src/components/StratItem.module.css
Normal file
0
src/components/StratItem.module.css
Normal file
57
src/components/StratItem.tsx
Normal file
57
src/components/StratItem.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
import styles from './StratItem.module.css';
|
||||
import { Component } from "solid-js";
|
||||
import { Group, Strat, api } from "../api";
|
||||
import { showDialog, showMessageDialog } from "../state";
|
||||
import { errorToString } from "../util";
|
||||
import { BsTrash } from 'solid-icons/bs';
|
||||
|
||||
export interface StratItemProps {
|
||||
group: Group,
|
||||
strat: Strat,
|
||||
onDelete: (m: Strat) => void
|
||||
}
|
||||
|
||||
const StratItem: Component<StratItemProps> = (props) => {
|
||||
|
||||
const deleteMember = async () => {
|
||||
showDialog({
|
||||
title: 'Remove strat',
|
||||
text: 'Do you want to remove this strat?',
|
||||
buttons: [
|
||||
{
|
||||
name: 'Remove',
|
||||
type: 'danger',
|
||||
action: async () => {
|
||||
try {
|
||||
await api().removeStrat(props.group.id, props.strat.id);
|
||||
props.onDelete(props.strat);
|
||||
} catch (e) {
|
||||
showMessageDialog('Failed to remove strat', errorToString(e));
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Cancel',
|
||||
action: () => { }
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={styles.member}>
|
||||
<div class={styles.memberDetails}>
|
||||
<div class={styles.memberName}>
|
||||
<span innerText={props.strat.title} />
|
||||
<span innerText={props.strat.stratType} />
|
||||
<span innerText={props.strat.map.name} />
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={deleteMember}><BsTrash /></button>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
export default StratItem;
|
0
src/components/StratTypeItem.module.css
Normal file
0
src/components/StratTypeItem.module.css
Normal file
55
src/components/StratTypeItem.tsx
Normal file
55
src/components/StratTypeItem.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import styles from './MapItem.module.css';
|
||||
import { Component, createSignal } from "solid-js";
|
||||
import { Group, Map, User, api } from "../api";
|
||||
import { showDialog, showMessageDialog } from "../state";
|
||||
import { errorToString } from "../util";
|
||||
import { BsTrash } from 'solid-icons/bs';
|
||||
|
||||
export interface StratTypeItem {
|
||||
group: Group,
|
||||
stratType: string,
|
||||
onDelete: (m: string) => void
|
||||
}
|
||||
|
||||
const StratTypeItem: Component<StratTypeItem> = (props) => {
|
||||
|
||||
const deleteStratType = async () => {
|
||||
showDialog({
|
||||
title: 'Remove strat type',
|
||||
text: 'Do you want to remove this strat type?',
|
||||
buttons: [
|
||||
{
|
||||
name: 'Remove',
|
||||
type: 'danger',
|
||||
action: async () => {
|
||||
try {
|
||||
await api().removeStratType(props.group.id, props.stratType);
|
||||
props.onDelete(props.stratType);
|
||||
} catch (e) {
|
||||
showMessageDialog('Failed to remove strat type', errorToString(e));
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Cancel',
|
||||
action: () => { }
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={styles.stratType}>
|
||||
<div class={styles.stratTypeDetails}>
|
||||
<div class={styles.stratTypeName}>
|
||||
<span innerText={props.stratType} />
|
||||
</div>
|
||||
<div>
|
||||
<button onClick={deleteStratType}><BsTrash /></button>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
export default StratTypeItem;
|
@ -2,10 +2,16 @@ import { useNavigate, useParams } from "@solidjs/router";
|
||||
import { Component, Show, createEffect, createSignal } from "solid-js";
|
||||
|
||||
import styles from './GroupPage.module.css'
|
||||
import { Group, User, api } from "../api";
|
||||
import { Group, Map, Profile, Strat, User, api } from "../api";
|
||||
import { showDialog, showInputDialog, showMessageDialog } from "../state";
|
||||
import { errorToString, normalize } from "../util";
|
||||
import { BsCheck2, BsPencil, BsTrash } from "solid-icons/bs";
|
||||
import { Collapse } from "solid-collapse";
|
||||
import MemberItem from "../components/MemberItem";
|
||||
import MapItem from "../components/MapItem";
|
||||
import StratItem from "../components/StratItem";
|
||||
import StratTypeItem from "../components/StratTypeItem";
|
||||
import ProfileItem from "../components/ProfileItem";
|
||||
|
||||
const GroupPage: Component = () => {
|
||||
const params = useParams();
|
||||
@ -16,6 +22,12 @@ const GroupPage: Component = () => {
|
||||
const [editing, setEditing] = createSignal(false);
|
||||
const [name, setName] = createSignal('');
|
||||
|
||||
const [membersIsExpanded, setMembersIsExpanded] = createSignal(false);
|
||||
const [mapsIsExpanded, setMapsIsExpanded] = createSignal(false);
|
||||
const [stratsIsExpanded, setStratsIsExpanded] = createSignal(false);
|
||||
const [profilesIsExpanded, setProfilesIsExpanded] = createSignal(false);
|
||||
const [stratTypesIsExpanded, setStratTypesIsExpanded] = createSignal(false);
|
||||
|
||||
createEffect(async () => {
|
||||
try {
|
||||
setGroup(await api().getGroup(params.id));
|
||||
@ -45,7 +57,7 @@ const GroupPage: Component = () => {
|
||||
type: 'danger',
|
||||
action: async () => {
|
||||
try {
|
||||
await api().deleteGroup(group().id);
|
||||
await api().removeGroup(group().id);
|
||||
navigate('/groups');
|
||||
} catch (e) {
|
||||
showMessageDialog('Failed to delete group', errorToString(e));
|
||||
@ -72,41 +84,6 @@ const GroupPage: Component = () => {
|
||||
|
||||
};
|
||||
|
||||
const addWishlist = async () => {
|
||||
const userWishlists = await api().getWishlists(api().ensureLoggedIn().username);
|
||||
showDialog({
|
||||
title: 'Add Wishlist', text: 'add wishlist to group', buttons:
|
||||
[...userWishlists.map(w => {
|
||||
return {
|
||||
name: w.title, action: async () => {
|
||||
try {
|
||||
const newGroup = await api().addWishlistToGroup(group().id, w.id);
|
||||
setGroup(newGroup);
|
||||
} catch (e) {
|
||||
showMessageDialog('Failed to add wishlist', errorToString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
'cancel'
|
||||
]
|
||||
})
|
||||
};
|
||||
|
||||
const removeWishlist = async (wishlistId: string) => {
|
||||
showDialog({
|
||||
title: 'Remove Wishlist', text: 'remove wishlist from group', buttons:
|
||||
[{
|
||||
name: 'remove', action: async () => {
|
||||
const newGroup = await api().removeWishlistFromGroup(group().id, wishlistId);
|
||||
setGroup(newGroup);
|
||||
}
|
||||
},
|
||||
'cancel'
|
||||
]
|
||||
})
|
||||
};
|
||||
|
||||
const toggleEdit = async () => {
|
||||
if (!editing()) {
|
||||
setEditing(true);
|
||||
@ -121,6 +98,22 @@ const GroupPage: Component = () => {
|
||||
setGroup({ ...group(), members: [...group().members.filter(m => m.username !== member.username)] });
|
||||
};
|
||||
|
||||
const onDeleteMap = async (map: Map) => {
|
||||
setGroup({ ...group(), maps: [...group().maps.filter(m => m.id !== map.id)] });
|
||||
};
|
||||
|
||||
const onDeleteStrat = async (strat: Strat) => {
|
||||
setGroup({ ...group(), strats: [...group().strats.filter(s => s.id !== strat.id)] });
|
||||
};
|
||||
|
||||
const onDeleteStratType = async (stratType: string) => {
|
||||
setGroup({ ...group(), stratTypes: [...group().stratTypes.filter(s => s !== stratType)] });
|
||||
};
|
||||
|
||||
const onDeleteProfile = async (profile: Profile) => {
|
||||
setGroup({ ...group(), profiles: [...group().profiles.filter(p => p.id !== profile.id)] });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Show when={!loading()} fallback={<div>Loading...</div>}>
|
||||
@ -129,20 +122,42 @@ const GroupPage: Component = () => {
|
||||
{editing() && <span class={styles.editCount}>{name().length}/50</span>}
|
||||
</div>
|
||||
<div class={styles.groupButtons}>
|
||||
<button class={styles.wishlistButton} onClick={deleteGroup}>Delete group</button>
|
||||
<button class={styles.deleteButton} onClick={deleteGroup}>Delete group</button>
|
||||
<button class={styles.editButton} onClick={toggleEdit}>{editing() ? <BsCheck2 /> : <BsPencil />}</button>
|
||||
</div>
|
||||
<hr />
|
||||
<button class={styles.groupButton} onClick={addMember}>Add Member</button>
|
||||
<div class={styles.groupMembers}>
|
||||
{group().members?.map(m => <MemberItem group={group()} member={m} onDelete={onDeleteMember} />)}
|
||||
</div>
|
||||
<button class={styles.wishlistButton} onClick={addWishlist}>Add Wishlist</button>
|
||||
<div class={styles.groupWishlists}>
|
||||
{group().wishlists?.map(w => <div>
|
||||
<WishlistItem wishlist={w} />
|
||||
<button class={styles.removeWishlistButton} onClick={() => removeWishlist(w.id)}><BsTrash /></button>
|
||||
</div>)}
|
||||
<div>
|
||||
<button onClick={() => setMembersIsExpanded(!membersIsExpanded())}>Members</button>
|
||||
<Collapse value={membersIsExpanded()} class={styles.members}>
|
||||
<button class={styles.addMemberButton} onClick={addMember}>Add Member</button>
|
||||
<div class={styles.groupMembers}>
|
||||
{group().members?.map(m => <MemberItem group={group()} member={m} onDelete={onDeleteMember} />)}
|
||||
</div>
|
||||
</Collapse>
|
||||
<button onClick={() => setStratsIsExpanded(!stratsIsExpanded())}>strats</button>
|
||||
<Collapse value={stratsIsExpanded()} class={styles.strats}>
|
||||
<div class={styles.strats}>
|
||||
{group().strats?.map(s => <StratItem group={group()} strat={s} onDelete={onDeleteStrat} />)}
|
||||
</div>
|
||||
</Collapse>
|
||||
<button onClick={() => setMapsIsExpanded(!mapsIsExpanded())}>maps</button>
|
||||
<Collapse value={mapsIsExpanded()} class={styles.maps}>
|
||||
<div class={styles.maps}>
|
||||
{group().maps?.map(m => <MapItem group={group()} map={m} onDelete={onDeleteMap} />)}
|
||||
</div>
|
||||
</Collapse>
|
||||
<button onClick={() => setStratTypesIsExpanded(!stratTypesIsExpanded())}>strat types</button>
|
||||
<Collapse value={stratTypesIsExpanded()} class={styles.stratTypes}>
|
||||
<div class={styles.stratTypes}>
|
||||
{group().stratTypes?.map(s => <StratTypeItem group={group()} stratType={s} onDelete={onDeleteStratType} />)}
|
||||
</div>
|
||||
</Collapse>
|
||||
<button onClick={() => setProfilesIsExpanded(!profilesIsExpanded())}>profiles</button>
|
||||
<Collapse value={profilesIsExpanded()} class={styles.profiles}>
|
||||
<div class={styles.profiles}>
|
||||
{group().profiles?.map(p => <ProfileItem group={group()} profile={p} onDelete={onDeleteProfile} />)}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
</Show>
|
||||
</>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, For, createSignal } from "solid-js";
|
||||
import { Component, For, Show, createSignal } from "solid-js";
|
||||
|
||||
import GroupItem from '../components/GroupItem'
|
||||
|
||||
@ -35,16 +35,18 @@ const Groups: Component = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={styles.page}>
|
||||
<h1>Your Groups</h1>
|
||||
<button onclick={createGroup}>Create Group</button>
|
||||
<hr />
|
||||
<div class={styles.groups}>
|
||||
<For each={groups()}>
|
||||
{g => <GroupItem group={g} />}
|
||||
</For>
|
||||
<Show when={!loading()} fallback={<div>Loading...</div>}>
|
||||
<div class={styles.page}>
|
||||
<h1>Your Groups</h1>
|
||||
<button onclick={createGroup}>Create Group</button>
|
||||
<hr />
|
||||
<div class={styles.groups}>
|
||||
<For each={groups()}>
|
||||
{g => <GroupItem group={g} />}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Show >
|
||||
)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user