diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/icon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrIcon.tsx similarity index 50% rename from apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/icon.tsx rename to apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrIcon.tsx index f51598c4..bbdd8e3d 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/icon.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrIcon.tsx @@ -1,12 +1,12 @@ import cx from 'classnames'; import type { ReactNode } from 'react'; -import type { IconProps } from '@/services/juxt-web/views/web/components/icon'; +import type { IconProps } from '@/services/juxt-web/views/web/components/ui/WebIcon'; export function CtrIcon(props: IconProps): ReactNode { - const baseClass = props.baseClass ?? 'icon'; + const type = props.type ?? 'icon'; return ( - - + + ); } diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/mii-icon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrMiiIcon.tsx similarity index 82% rename from apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/mii-icon.tsx rename to apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrMiiIcon.tsx index d4993d64..a6f6e2cf 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/mii-icon.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/components/ui/CtrMiiIcon.tsx @@ -1,19 +1,19 @@ import { utils } from '@/services/juxt-web/views/utils'; -import { CtrIcon } from '@/services/juxt-web/views/ctr/components/icon'; +import { CtrIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrIcon'; import type { ReactNode } from 'react'; -import type { MiiIconProps } from '@/services/juxt-web/views/web/components/mii-icon'; +import type { MiiIconProps } from '@/services/juxt-web/views/web/components/ui/WebMiiIcon'; export function CtrMiiIcon(props: MiiIconProps): ReactNode { const url = props.face_url ?? utils.cdn(props.ctx, `/mii/${props.pid}/normal_face.png`); const href = `/users/${props.pid}`; - const baseClass = !props.big ? 'mii-icon' : undefined; + const type = props.type ?? 'mii-icon'; return ( diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messageThread.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messageThread.tsx index 875e7388..ab0abeda 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messageThread.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messageThread.tsx @@ -3,7 +3,7 @@ import cx from 'classnames'; import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root'; import { CtrNewPostView } from '@/services/juxt-web/views/ctr/newPostView'; import { utils } from '@/services/juxt-web/views/utils'; -import { CtrMiiIcon } from '@/services/juxt-web/views/ctr/components/mii-icon'; +import { CtrMiiIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrMiiIcon'; import type { ReactNode } from 'react'; import type { MessageThreadItemProps, MessageThreadViewProps } from '@/services/juxt-web/views/web/messageThread'; diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messages.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messages.tsx index 65e3851a..0ba684ca 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messages.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/messages.tsx @@ -1,5 +1,5 @@ import cx from 'classnames'; -import { CtrMiiIcon } from '@/services/juxt-web/views/ctr/components/mii-icon'; +import { CtrMiiIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrMiiIcon'; import { humanFromNow } from '@/util'; import type { ReactNode } from 'react'; import type { @@ -40,7 +40,7 @@ export function CtrMessagesView(props: MessagesViewProps): ReactNode { diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/notificationListView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/notificationListView.tsx index b4e132c3..c0530544 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/notificationListView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/notificationListView.tsx @@ -1,7 +1,7 @@ import cx from 'classnames'; import { CtrPageBody, CtrRoot } from '@/services/juxt-web/views/ctr/root'; -import { CtrMiiIcon } from '@/services/juxt-web/views/ctr/components/mii-icon'; -import { CtrIcon } from '@/services/juxt-web/views/ctr/components/icon'; +import { CtrMiiIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrMiiIcon'; +import { CtrIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrIcon'; import { humanFromNow } from '@/util'; import type { ReactNode } from 'react'; import type { NotificationItemProps, NotificationListViewProps, NotificationWrapperViewProps } from '@/services/juxt-web/views/web/notificationListView'; @@ -12,7 +12,7 @@ function CtrNotificationItem(props: NotificationItemProps): ReactNode { const NickName = ({ userId }: { userId: string | number | null | undefined }): ReactNode => {userId ? props.ctx.usersMap.get(Number(userId)) : null}; return ( <> - +

{notif.users.length === 1 diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/post.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/post.tsx index 7ab3160c..ddb7521e 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/post.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/ctr/post.tsx @@ -1,7 +1,7 @@ import cx from 'classnames'; import moment from 'moment'; import { utils } from '@/services/juxt-web/views/utils'; -import { CtrMiiIcon } from '@/services/juxt-web/views/ctr/components/mii-icon'; +import { CtrMiiIcon } from '@/services/juxt-web/views/ctr/components/ui/CtrMiiIcon'; import type { ReactNode } from 'react'; import type { PostScreenshotProps, PostViewProps } from '@/services/juxt-web/views/web/post'; diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityView.tsx index 254a0e0a..2011b80c 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/communityView.tsx @@ -4,7 +4,7 @@ import { PortalPageBody, PortalRoot } from '@/services/juxt-web/views/portal/roo import { PortalNavBar } from '@/services/juxt-web/views/portal/navbar'; import { PortalNewPostView } from '@/services/juxt-web/views/portal/newPostView'; import { PortalPostListClosedView } from '@/services/juxt-web/views/portal/postList'; -import { PortalIcon } from '@/services/juxt-web/views/portal/icons'; +import { PortalUIIcon } from '@/services/juxt-web/views/portal/components/ui/PortalUIIcon'; import type { ReactNode } from 'react'; import type { CommunityViewProps } from '@/services/juxt-web/views/web/communityView'; @@ -71,13 +71,13 @@ export function PortalCommunityView(props: CommunityViewProps): ReactNode { {community.name} - + {' '} {props.totalPosts} {' | '} - + {' '} {community.followers} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/icon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/icon.tsx deleted file mode 100644 index 2f3df92d..00000000 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/icon.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import cx from 'classnames'; -import type { ReactNode } from 'react'; -import type { IconProps } from '@/services/juxt-web/views/web/components/icon'; - -export function PortalIconView(props: IconProps): ReactNode { - const baseClass = props.baseClass ?? 'icon'; - return ( - - - - ); -} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/ui/PortalIcon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/ui/PortalIcon.tsx new file mode 100644 index 00000000..a315e245 --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/ui/PortalIcon.tsx @@ -0,0 +1,12 @@ +import cx from 'classnames'; +import type { ReactNode } from 'react'; +import type { IconProps } from '@/services/juxt-web/views/web/components/ui/WebIcon'; + +export function PortalIcon(props: IconProps): ReactNode { + const type = props.type ?? 'icon'; + return ( + + + + ); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/mii-icon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/ui/PortalMiiIcon.tsx similarity index 66% rename from apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/mii-icon.tsx rename to apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/ui/PortalMiiIcon.tsx index bd54c797..ed20da01 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/mii-icon.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/ui/PortalMiiIcon.tsx @@ -1,21 +1,21 @@ import { utils } from '@/services/juxt-web/views/utils'; -import { PortalIconView } from '@/services/juxt-web/views/portal/components/icon'; +import { PortalIcon } from '@/services/juxt-web/views/portal/components/ui/PortalIcon'; import type { ReactNode } from 'react'; -import type { MiiIconProps } from '@/services/juxt-web/views/web/components/mii-icon'; +import type { MiiIconProps } from '@/services/juxt-web/views/web/components/ui/WebMiiIcon'; export function PortalMiiIcon(props: MiiIconProps): ReactNode { const url = props.face_url ?? utils.cdn(props.ctx, `/mii/${props.pid}/normal_face.png`); const href = `/users/${props.pid}`; - const baseClass = !props.big ? 'mii-icon' : undefined; + const type = props.type ?? 'mii-icon'; return ( - - + ); } diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/ui/PortalUIIcon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/ui/PortalUIIcon.tsx new file mode 100644 index 00000000..7bc12599 --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/components/ui/PortalUIIcon.tsx @@ -0,0 +1,36 @@ +/* eslint-disable no-restricted-imports -- raw import plugin does not support path aliases */ +import devBadgeIcon from '../../assets/dev_badge.svg?raw'; +import modBadgeIcon from '../../assets/mod_badge.svg?raw'; +import starBadgeIcon from '../../assets/star_badge.svg?raw'; +import testerBadgeIcon from '../../assets/tester_badge.svg?raw'; +import birthdayIcon from '../../assets/birthday.svg?raw'; +import countryIcon from '../../assets/country.svg?raw'; +import followersIcon from '../../assets/followers.svg?raw'; +import postsIcon from '../../assets/posts.svg?raw'; +import skillIcon from '../../assets/skill.svg?raw'; +import topicIcon from '../../assets/topic.svg?raw'; +import type { ReactNode } from 'react'; + +const icons = { + 'dev-badge': devBadgeIcon, + 'mod-badge': modBadgeIcon, + 'star-badge': starBadgeIcon, + 'tester-badge': testerBadgeIcon, + 'birthday': birthdayIcon, + 'country': countryIcon, + 'followers': followersIcon, + 'posts': postsIcon, + 'skill': skillIcon, + 'topic': topicIcon +} as const; + +type PortalUIIcon = keyof typeof icons; + +export type PortalUIIconProps = { + name: PortalUIIcon; +}; + +export function PortalUIIcon(props: PortalUIIconProps): ReactNode { + const iconHtml = icons[props.name]; + return ; +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/icons.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/icons.tsx deleted file mode 100644 index 06e33d59..00000000 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/icons.tsx +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable no-restricted-imports -- raw import plugin does not support path aliases */ -import devBadgeIcon from './assets/dev_badge.svg?raw'; -import modBadgeIcon from './assets/mod_badge.svg?raw'; -import starBadgeIcon from './assets/star_badge.svg?raw'; -import testerBadgeIcon from './assets/tester_badge.svg?raw'; -import birthdayIcon from './assets/birthday.svg?raw'; -import countryIcon from './assets/country.svg?raw'; -import followersIcon from './assets/followers.svg?raw'; -import postsIcon from './assets/posts.svg?raw'; -import skillIcon from './assets/skill.svg?raw'; -import topicIcon from './assets/topic.svg?raw'; -import type { ReactNode } from 'react'; - -const icons = { - 'dev-badge': devBadgeIcon, - 'mod-badge': modBadgeIcon, - 'star-badge': starBadgeIcon, - 'tester-badge': testerBadgeIcon, - 'birthday': birthdayIcon, - 'country': countryIcon, - 'followers': followersIcon, - 'posts': postsIcon, - 'skill': skillIcon, - 'topic': topicIcon -} as const; - -type PortalIcon = keyof typeof icons; - -export type PortalIconProps = { - name: PortalIcon; -}; - -export function PortalIcon(props: PortalIconProps): ReactNode { - const iconHtml = icons[props.name]; - return ; -} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/notificationListView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/notificationListView.tsx index f61bec5c..2f8c8c8c 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/notificationListView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/notificationListView.tsx @@ -2,8 +2,8 @@ import cx from 'classnames'; import { PortalPageBody, PortalRoot } from '@/services/juxt-web/views/portal/root'; import { PortalNavBar } from '@/services/juxt-web/views/portal/navbar'; import { humanFromNow } from '@/util'; -import { PortalMiiIcon } from '@/services/juxt-web/views/portal/components/mii-icon'; -import { PortalIconView } from '@/services/juxt-web/views/portal/components/icon'; +import { PortalMiiIcon } from '@/services/juxt-web/views/portal/components/ui/PortalMiiIcon'; +import { PortalIcon } from '@/services/juxt-web/views/portal/components/ui/PortalIcon'; import type { ReactNode } from 'react'; import type { NotificationItemProps, NotificationListViewProps, NotificationWrapperViewProps } from '@/services/juxt-web/views/web/notificationListView'; @@ -13,7 +13,7 @@ function PortalNotificationItem(props: NotificationItemProps): ReactNode { const NickName = ({ userId }: { userId: string | number | null | undefined }): ReactNode => {userId ? props.ctx.usersMap.get(Number(userId)) : null}; return ( <> - +

{notif.users.length === 1 @@ -63,7 +63,7 @@ function PortalNotificationItem(props: NotificationItemProps): ReactNode { if (notif.type === 'notice') { return ( <> - +

diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/post.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/post.tsx index c24457fe..0311866e 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/post.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/post.tsx @@ -1,7 +1,7 @@ import cx from 'classnames'; import moment from 'moment'; import { utils } from '@/services/juxt-web/views/utils'; -import { PortalIcon } from '@/services/juxt-web/views/portal/icons'; +import { PortalUIIcon } from '@/services/juxt-web/views/portal/components/ui/PortalUIIcon'; import type { ReactNode } from 'react'; import type { PostScreenshotProps, PostViewProps } from '@/services/juxt-web/views/web/post'; @@ -68,7 +68,7 @@ export function PortalPostView(props: PostViewProps): ReactNode { ? ( {/* TODO this has been modified due to inbalanced tags */} - + {post.topic_tag} ) diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/userPageView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/userPageView.tsx index 8d316047..92721c9e 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/portal/userPageView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/portal/userPageView.tsx @@ -3,7 +3,7 @@ import moment from 'moment'; import { utils } from '@/services/juxt-web/views/utils'; import { PortalPageBody, PortalRoot } from '@/services/juxt-web/views/portal/root'; import { PortalNavBar } from '@/services/juxt-web/views/portal/navbar'; -import { PortalIcon } from '@/services/juxt-web/views/portal/icons'; +import { PortalUIIcon } from '@/services/juxt-web/views/portal/components/ui/PortalUIIcon'; import type { ReactNode } from 'react'; import type { UserPageViewProps } from '@/services/juxt-web/views/web/userPageView'; @@ -16,7 +16,7 @@ export function PortalUserTier(props: { user: UserPageViewProps['user'] }): Reac tierPart = ( {' | '} - + ); } @@ -24,7 +24,7 @@ export function PortalUserTier(props: { user: UserPageViewProps['user'] }): Reac tierPart = ( {' | '} - + ); } @@ -32,7 +32,7 @@ export function PortalUserTier(props: { user: UserPageViewProps['user'] }): Reac tierPart = ( {' | '} - + ); } @@ -41,7 +41,7 @@ export function PortalUserTier(props: { user: UserPageViewProps['user'] }): Reac accessLevelPart = ( {' | '} - + ); } @@ -49,7 +49,7 @@ export function PortalUserTier(props: { user: UserPageViewProps['user'] }): Reac accessLevelPart = ( {' | '} - + ); } @@ -57,7 +57,7 @@ export function PortalUserTier(props: { user: UserPageViewProps['user'] }): Reac accessLevelPart = ( {' | '} - + ); } @@ -120,13 +120,13 @@ export function PortalUserPageView(props: UserPageViewProps): ReactNode { {' | '} - + {' '} {props.totalPosts} {' | '} - + {' '} {props.userContent.following_users.length - 1} @@ -134,7 +134,7 @@ export function PortalUserPageView(props: UserPageViewProps): ReactNode { ? ( {' | '} - + {' '} {props.user.country} @@ -144,7 +144,7 @@ export function PortalUserPageView(props: UserPageViewProps): ReactNode { ? ( {' | '} - + {' '} {moment.utc(props.user.birthdate).format('MMM Do')} @@ -154,7 +154,7 @@ export function PortalUserPageView(props: UserPageViewProps): ReactNode { ? ( {' | '} - + {' '} {props.userSettings.game_skill === 0 ? ( diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/editCommunityView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/editCommunityView.tsx index a0df0e44..e92a09f1 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/editCommunityView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/editCommunityView.tsx @@ -17,8 +17,6 @@ export function WebEditCommunityView(props: EditCommunityViewProps): ReactNode { const imageId = community.parent ? community.parent : community.olive_community_id; const head = ( <> - - Juxt - {community.name} @@ -27,7 +25,7 @@ export function WebEditCommunityView(props: EditCommunityViewProps): ReactNode { ); return ( - <WebRoot head={head}> + <WebRoot type="admin" head={head}> <h2 id="title" className="page-header"> Edit Community </h2> @@ -62,8 +60,9 @@ export function WebEditCommunityView(props: EditCommunityViewProps): ReactNode { </select> </div> <div className="col-md-9"> - <label className="labels" htmlFor="title_ids">Title IDs (comma separated list)</label> - <input id="title-ids" name="title_ids" type="text" className="form-control" placeholder="1407375153678336, 1407375153685760, 1407375153686016" value={community.title_id} /> + <label className="labels" htmlFor="title_ids">Title IDs (hex)</label> + <textarea rows={10} data-input-admin-title-ids="#title-ids"></textarea> + <input id="title-ids" name="title_ids" type="hidden" value={community.title_id} /> </div> <div className="col-md-3"> <label className="labels" htmlFor="browserIcon">Browser Icon (128px x 128px)</label> diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/manageCommunityView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/manageCommunityView.tsx index 701b6bd2..46573462 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/manageCommunityView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/manageCommunityView.tsx @@ -2,6 +2,7 @@ import { WebRoot, WebWrapper } from '@/services/juxt-web/views/web/root'; import { WebNavBar } from '@/services/juxt-web/views/web/navbar'; import { WebModerationTabs } from '@/services/juxt-web/views/web/admin/admin'; import { utils } from '@/services/juxt-web/views/utils'; +import { WebSearchBar } from '@/services/juxt-web/views/web/components/ui/WebSearchBar'; import type { ReactNode } from 'react'; import type { InferSchemaType } from 'mongoose'; import type { RenderContext } from '@/services/juxt-web/views/context'; @@ -16,12 +17,11 @@ export type ManageCommunityViewProps = { }; export function WebManageCommunityView(props: ManageCommunityViewProps): ReactNode { - const head = <script src="/js/admin.global.js"></script>; const prevUrl = utils.url('/admin/communities', { page: props.page - 1, search: props.search }); const nextUrl = utils.url('/admin/communities', { page: props.page + 1, search: props.search }); return ( - <WebRoot head={head}> + <WebRoot type="admin"> <h2 id="title" className="page-header"> Manage Communities </h2> @@ -29,7 +29,7 @@ export function WebManageCommunityView(props: ManageCommunityViewProps): ReactNo <div id="toast"></div> <WebWrapper> <WebModerationTabs ctx={props.ctx} selected="communities" /> - <input type="string" id="community-search" className="searchbar" placeholder="Search..." value={props.search} /> + <WebSearchBar ctx={props.ctx} search={props.search} /> <button style={{ marginTop: '1em' }}> <a href="/admin/communities/new" className="button">Create Community</a> </button> diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/moderateUserView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/moderateUserView.tsx index fa3f506a..d1c1da95 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/moderateUserView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/moderateUserView.tsx @@ -32,14 +32,12 @@ export function WebModerateUserView(props: ModerateUserViewProps): ReactNode { const pnidName = props.pnid.mii?.name ?? props.pnid.username; const head = ( <> - <script src="/js/admin.global.js"></script> - <link rel="stylesheet" href="/css/admin.css" /> <WebUserPageMeta ctx={props.ctx} user={props.pnid} userSettings={props.userSettings} withImage /> </> ); return ( - <WebRoot head={head}> + <WebRoot type="admin" head={head}> <h2 id="title" className="page-header"> {props.ctx.lang.global.user_page} </h2> @@ -134,7 +132,7 @@ export function WebModerateUserView(props: ModerateUserViewProps): ReactNode { </div> </div> <div className="mt-5 text-center"> - <button className="btn btn-primary profile-button" type="button" evt-click={`savePNID(${props.userSettings.pid})`}>Save User</button> + <button className="btn btn-primary profile-button" type="button" data-button-admin-save-pnid={props.userSettings.pid}>Save User</button> </div> </div> <details open> diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/newCommunityView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/newCommunityView.tsx index 66199609..cdbb2b00 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/newCommunityView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/newCommunityView.tsx @@ -11,14 +11,12 @@ export type NewCommunityViewProps = { export function WebNewCommunityView(props: NewCommunityViewProps): ReactNode { const head = ( <> - <script src="/js/admin.global.js"></script> - <link rel="stylesheet" href="/css/admin.css" /> <title>Juxt - New Community ); return ( - +

New Community

diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/reportListView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/reportListView.tsx index 274d05c8..3bf2efb4 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/reportListView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/reportListView.tsx @@ -1,9 +1,9 @@ -import moment from 'moment'; import { WebRoot, WebWrapper } from '@/services/juxt-web/views/web/root'; import { WebNavBar } from '@/services/juxt-web/views/web/navbar'; import { WebModerationTabs } from '@/services/juxt-web/views/web/admin/admin'; -import { utils } from '@/services/juxt-web/views/utils'; import { WebPostView } from '@/services/juxt-web/views/web/post'; +import { humanDate, humanFromNow } from '@/util'; +import { WebMiiIcon } from '@/services/juxt-web/views/web/components/ui/WebMiiIcon'; import type { ReactNode } from 'react'; import type { InferSchemaType } from 'mongoose'; import type { RenderContext } from '@/services/juxt-web/views/context'; @@ -32,21 +32,23 @@ export type ReportProps = { }; function Report(props: ReportProps): ReactNode { + const reporter = props.report.reported_by; + return (
  • - - - + - - Reported By: - {props.ctx.usersMap.get(props.report.reported_by)} - - {moment(props.report.created_at).fromNow()} + + {`Reported by ${props.ctx.usersMap.get(reporter)}`} + + {' - '} + {reporter} + {' - '} + {humanFromNow(props.report.created_at)}

    {props.reasonMap[props.report.reason] ?? 'Unknown'}

    @@ -59,8 +61,8 @@ function Report(props: ReportProps): ReactNode {
    - - + +
  • @@ -68,10 +70,8 @@ function Report(props: ReportProps): ReactNode { } export function WebReportListView(props: ReportListViewProps): ReactNode { - const head = ; - return ( - +

    User Reports ( {props.reports.length} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/userListView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/userListView.tsx index 0d8b129a..45055035 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/userListView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/admin/userListView.tsx @@ -2,6 +2,7 @@ import { WebRoot, WebWrapper } from '@/services/juxt-web/views/web/root'; import { WebNavBar } from '@/services/juxt-web/views/web/navbar'; import { WebModerationTabs } from '@/services/juxt-web/views/web/admin/admin'; import { utils } from '@/services/juxt-web/views/utils'; +import { WebSearchBar } from '@/services/juxt-web/views/web/components/ui/WebSearchBar'; import type { ReactNode } from 'react'; import type { RenderContext } from '@/services/juxt-web/views/context'; import type { HydratedSettingsDocument } from '@/models/settings'; @@ -16,12 +17,11 @@ export type UserListViewProps = { }; export function WebUserListView(props: UserListViewProps): ReactNode { - const head = ; const prevUrl = utils.url('/admin/accounts', { page: props.page - 1, search: props.search }); const nextUrl = utils.url('/admin/accounts', { page: props.page + 1, search: props.search }); return ( - +

    User Accounts

    @@ -29,7 +29,7 @@ export function WebUserListView(props: UserListViewProps): ReactNode {
    - + { props.userCount } {' '} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/icon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/icon.tsx deleted file mode 100644 index dd411cd0..00000000 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/icon.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { RenderContext } from '@/services/juxt-web/views/context'; - -export type IconProps = { - ctx: RenderContext; - - src: string; - href?: string; - baseClass?: string; // default ".icon" - className?: string; // extra classes -}; diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/mii-icon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/mii-icon.tsx deleted file mode 100644 index 8f801bf2..00000000 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/mii-icon.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import type { RenderContext } from '@/services/juxt-web/views/context'; - -export type MiiIconProps = { - ctx: RenderContext; - - pid: number; - face_url?: string; - big?: boolean; // Use .icon (Icon) instead of .mii-icon - className?: string; // extra classes -}; diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebIcon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebIcon.tsx new file mode 100644 index 00000000..1045aad2 --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebIcon.tsx @@ -0,0 +1,22 @@ +import cx from 'classnames'; +import type { ReactNode } from 'react'; +import type { RenderContext } from '@/services/juxt-web/views/context'; + +export type IconProps = { + ctx: RenderContext; + + src: string; + href?: string; + + type?: 'icon' | 'mii-icon'; // default ".icon" + className?: string; // extra classes +}; + +export function WebIcon(props: IconProps): ReactNode { + const type = props.type ?? 'icon'; + return ( + + + + ); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebMiiIcon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebMiiIcon.tsx new file mode 100644 index 00000000..365a62e6 --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebMiiIcon.tsx @@ -0,0 +1,31 @@ +import { utils } from '@/services/juxt-web/views/utils'; +import { WebIcon } from '@/services/juxt-web/views/web/components/ui/WebIcon'; +import type { ReactNode } from 'react'; +import type { RenderContext } from '@/services/juxt-web/views/context'; + +export type MiiIconProps = { + ctx: RenderContext; + + pid: number; + face_url?: string; + + type?: 'mii-icon' | 'icon'; // default ".mii-icon" + className?: string; // extra classes +}; + +export function WebMiiIcon(props: MiiIconProps): ReactNode { + const url = props.face_url ?? utils.cdn(props.ctx, `/mii/${props.pid}/normal_face.png`); + const href = `/users/${props.pid}`; + const type = props.type ?? 'mii-icon'; + + return ( + + + ); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebSearchBar.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebSearchBar.tsx new file mode 100644 index 00000000..9ce1cd5e --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebSearchBar.tsx @@ -0,0 +1,21 @@ +import { WebUIIcon } from '@/services/juxt-web/views/web/components/ui/WebUIIcon'; +import type { ReactNode } from 'react'; +import type { RenderContext } from '@/services/juxt-web/views/context'; + +export type WebSearchBarProps = { + ctx: RenderContext; + + search?: string; +}; + +export function WebSearchBar(props: WebSearchBarProps): ReactNode { + return ( +
    + + +
    + ); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebUIIcon.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebUIIcon.tsx new file mode 100644 index 00000000..f9e5d92a --- /dev/null +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/components/ui/WebUIIcon.tsx @@ -0,0 +1,67 @@ +/* eslint-disable no-restricted-imports -- raw import plugin does not support path aliases */ +import heartIcon from '../../assets/heart_icon.svg?raw'; +import replyIcon from '../../assets/reply_icon.svg?raw'; +import menuIcon from '../../assets/menu_icon.svg?raw'; +import flagIcon from '../../assets/flag_icon.svg?raw'; +import binIcon from '../../assets/bin_icon.svg?raw'; +import shareIcon from '../../assets/share_icon.svg?raw'; +import devBadgeIcon from '../../assets/dev_badge.svg?raw'; +import modBadgeIcon from '../../assets/mod_badge.svg?raw'; +import starBadgeIcon from '../../assets/star_badge.svg?raw'; +import testerBadgeIcon from '../../assets/tester_badge.svg?raw'; +import juxtLogoSvg from '../../assets/juxt_logo.svg?raw'; +import pretendoLogoSvg from '../../assets/pretendo_logo.svg?raw'; +import mailIcon from '../../assets/mail.svg?raw'; +import bellIcon from '../../assets/bell.svg?raw'; +import usersIcon from '../../assets/users.svg?raw'; +import homeIcon from '../../assets/home.svg?raw'; +import hammerIcon from '../../assets/hammer.svg?raw'; +import type { ReactNode } from 'react'; + +const icons = { + 'dev-badge': devBadgeIcon, + 'mod-badge': modBadgeIcon, + 'star-badge': starBadgeIcon, + 'tester-badge': testerBadgeIcon, + 'heart': heartIcon, + 'reply': replyIcon, + 'menu': menuIcon, + 'flag': flagIcon, + 'bin': binIcon, + 'share': shareIcon, + 'mail': mailIcon, + 'bell': bellIcon, + 'users': usersIcon, + 'home': homeIcon, + 'hammer': hammerIcon +} as const; + +type WebUIIcon = keyof typeof icons; + +export type WebUIIconProps = { + name: WebUIIcon; +}; + +export function WebUIIcon(props: WebUIIconProps): ReactNode { + const iconHtml = icons[props.name]; + return ( + + ); +} + +export function PretendoLogo(): ReactNode { + return ( + + ); +} + +export function JuxtLogo(): ReactNode { + return ( + + ); +} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/errorView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/errorView.tsx index 90c2d08d..3c17d323 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/errorView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/errorView.tsx @@ -1,5 +1,5 @@ import { WebLoginRoot } from '@/services/juxt-web/views/web/login'; -import { PretendoLogo } from '@/services/juxt-web/views/web/icons'; +import { PretendoLogo } from '@/services/juxt-web/views/web/components/ui/WebUIIcon'; import type { ReactNode } from 'react'; import type { ReqId } from 'pino-http'; import type { RenderContext } from '@/services/juxt-web/views/context'; diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/icons.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/icons.tsx deleted file mode 100644 index efa58915..00000000 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/icons.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* eslint-disable no-restricted-imports -- raw import plugin does not support path aliases */ -import heartIcon from './assets/heart_icon.svg?raw'; -import replyIcon from './assets/reply_icon.svg?raw'; -import menuIcon from './assets/menu_icon.svg?raw'; -import flagIcon from './assets/flag_icon.svg?raw'; -import binIcon from './assets/bin_icon.svg?raw'; -import shareIcon from './assets/share_icon.svg?raw'; -import devBadgeIcon from './assets/dev_badge.svg?raw'; -import modBadgeIcon from './assets/mod_badge.svg?raw'; -import starBadgeIcon from './assets/star_badge.svg?raw'; -import testerBadgeIcon from './assets/tester_badge.svg?raw'; -import juxtLogoSvg from './assets/juxt_logo.svg?raw'; -import pretendoLogoSvg from './assets/pretendo_logo.svg?raw'; -import mailIcon from './assets/mail.svg?raw'; -import bellIcon from './assets/bell.svg?raw'; -import usersIcon from './assets/users.svg?raw'; -import homeIcon from './assets/home.svg?raw'; -import hammerIcon from './assets/hammer.svg?raw'; -import type { ReactNode } from 'react'; - -const icons = { - 'dev-badge': devBadgeIcon, - 'mod-badge': modBadgeIcon, - 'star-badge': starBadgeIcon, - 'tester-badge': testerBadgeIcon, - 'heart': heartIcon, - 'reply': replyIcon, - 'menu': menuIcon, - 'flag': flagIcon, - 'bin': binIcon, - 'share': shareIcon, - 'mail': mailIcon, - 'bell': bellIcon, - 'users': usersIcon, - 'home': homeIcon, - 'hammer': hammerIcon -} as const; - -type WebIcon = keyof typeof icons; - -export type WebIconProps = { - name: WebIcon; -}; - -export function WebIcon(props: WebIconProps): ReactNode { - const iconHtml = icons[props.name]; - return ( - - ); -} - -export function PretendoLogo(): ReactNode { - return ( - - ); -} - -export function JuxtLogo(): ReactNode { - return ( - - ); -} diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/loginView.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/loginView.tsx index c077f44c..9f324bb8 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/loginView.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/loginView.tsx @@ -1,5 +1,5 @@ import { WebLoginRoot } from '@/services/juxt-web/views/web/login'; -import { PretendoLogo } from '@/services/juxt-web/views/web/icons'; +import { PretendoLogo } from '@/services/juxt-web/views/web/components/ui/WebUIIcon'; import type { ReactNode } from 'react'; import type { RenderContext } from '@/services/juxt-web/views/context'; diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/navbar.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/navbar.tsx index 347fc68f..dfa0f14e 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/navbar.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/navbar.tsx @@ -1,5 +1,5 @@ import { utils } from '@/services/juxt-web/views/utils'; -import { JuxtLogo, WebIcon } from '@/services/juxt-web/views/web/icons'; +import { JuxtLogo, WebUIIcon } from '@/services/juxt-web/views/web/components/ui/WebUIIcon'; import type { ReactNode } from 'react'; import type { RenderContext } from '@/services/juxt-web/views/context'; @@ -29,20 +29,20 @@ export function WebNavBar(props: NavBarProps): ReactNode {

    {props.ctx.lang.global.user_page}

    - +

    {props.ctx.lang.global.activity_feed}

    - +

    {props.ctx.lang.global.communities}

    - +

    {props.ctx.lang.global.messages}

    - +

    {props.ctx.lang.global.notifications}

    @@ -50,7 +50,7 @@ export function WebNavBar(props: NavBarProps): ReactNode { ? ( <> - +

    Moderation

    diff --git a/apps/juxtaposition-ui/src/services/juxt-web/views/web/post.tsx b/apps/juxtaposition-ui/src/services/juxt-web/views/web/post.tsx index 24e8b83e..d709c1b9 100644 --- a/apps/juxtaposition-ui/src/services/juxt-web/views/web/post.tsx +++ b/apps/juxtaposition-ui/src/services/juxt-web/views/web/post.tsx @@ -1,7 +1,7 @@ import cx from 'classnames'; import moment from 'moment'; import { utils } from '@/services/juxt-web/views/utils'; -import { WebIcon } from '@/services/juxt-web/views/web/icons'; +import { WebUIIcon } from '@/services/juxt-web/views/web/components/ui/WebUIIcon'; import type { InferSchemaType } from 'mongoose'; import type { ReactNode } from 'react'; import type { ContentSchema } from '@/models/content'; @@ -109,7 +109,7 @@ export function WebPostView(props: PostViewProps): ReactNode { role="button" aria-pressed={yeahed} > - +

    {post.empathy_count}

    @@ -119,30 +119,30 @@ export function WebPostView(props: PostViewProps): ReactNode { className="post-button reply-button" role="button" > - +

    {post.reply_count}

    {/* Hamburger menu */}