diff --git a/package-lock.json b/package-lock.json index b842915..2ad4bfa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 95bf7fa..e477655 100644 --- a/package.json +++ b/package.json @@ -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" } diff --git a/src/App.tsx b/src/App.tsx index baf7c57..bf507a8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -60,7 +60,6 @@ const AppLayout: Component = (props) => {
diff --git a/src/api.ts b/src/api.ts index 7eea69a..b98e50c 100644 --- a/src/api.ts +++ b/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 { + 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 { 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 { + public async removeProfileFromGroup(groupId: string, profileId: string): Promise { const response = await fetch(this.apiURL + '/group/' + groupId + '/profile/' + profileId, { method: 'DELETE', headers: { diff --git a/src/components/MapItem.module.css b/src/components/MapItem.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/MapItem.tsx b/src/components/MapItem.tsx new file mode 100644 index 0000000..5a5b1d4 --- /dev/null +++ b/src/components/MapItem.tsx @@ -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 = (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 ( +
+
+
+ +
+
+ +
+
+
+ ) +} + +export default MapItem; diff --git a/src/components/MemberItem.module.css b/src/components/MemberItem.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/MemberItem.tsx b/src/components/MemberItem.tsx new file mode 100644 index 0000000..73e6477 --- /dev/null +++ b/src/components/MemberItem.tsx @@ -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 = (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 ( +
+
+
+ +
+
+ +
+
+
+ ) +} + +export default MemberItem; diff --git a/src/components/ProfileItem.module.css b/src/components/ProfileItem.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/ProfileItem.tsx b/src/components/ProfileItem.tsx new file mode 100644 index 0000000..a6f9d8d --- /dev/null +++ b/src/components/ProfileItem.tsx @@ -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 = (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 ( +
+
+
+ +
+
+ +
+
+
+ ) +} + +export default ProfileItem; diff --git a/src/components/StratItem.module.css b/src/components/StratItem.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/StratItem.tsx b/src/components/StratItem.tsx new file mode 100644 index 0000000..814d07d --- /dev/null +++ b/src/components/StratItem.tsx @@ -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 = (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 ( +
+
+
+ + + +
+
+ +
+
+
+ ) +} + +export default StratItem; diff --git a/src/components/StratTypeItem.module.css b/src/components/StratTypeItem.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/StratTypeItem.tsx b/src/components/StratTypeItem.tsx new file mode 100644 index 0000000..075f9dc --- /dev/null +++ b/src/components/StratTypeItem.tsx @@ -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 = (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 ( +
+
+
+ +
+
+ +
+
+
+ ) +} + +export default StratTypeItem; diff --git a/src/pages/GroupPage.tsx b/src/pages/GroupPage.tsx index 8e86b2e..885098f 100644 --- a/src/pages/GroupPage.tsx +++ b/src/pages/GroupPage.tsx @@ -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 ( <> Loading...
}> @@ -129,20 +122,42 @@ const GroupPage: Component = () => { {editing() && {name().length}/50}
- +

- -
- {group().members?.map(m => )} -
- -
- {group().wishlists?.map(w =>
- - -
)} +
+ + + +
+ {group().members?.map(m => )} +
+
+ + +
+ {group().strats?.map(s => )} +
+
+ + +
+ {group().maps?.map(m => )} +
+
+ + +
+ {group().stratTypes?.map(s => )} +
+
+ + +
+ {group().profiles?.map(p => )} +
+
diff --git a/src/pages/Groups.tsx b/src/pages/Groups.tsx index f6c0452..6b5ed77 100644 --- a/src/pages/Groups.tsx +++ b/src/pages/Groups.tsx @@ -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 ( -
-

Your Groups

- -
-
- - {g => } - + Loading...
}> +
+

Your Groups

+ +
+
+ + {g => } + +
-
+ ) }