Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions components/Activity/AgendaList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Agenda } from '@open-source-bazaar/activityhub-service';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import { ObservedComponent } from 'mobx-react-helper';
import { Column, RestTable } from 'mobx-restful-table';
import { Badge } from 'react-bootstrap';

import { AgendaModel } from '../../models/Agenda';
import placeStore from '../../models/Place';
import { i18n, I18nContext } from '../../models/Translation';
import userStore from '../../models/User';
import { renderTagInput } from '../Tag';

export interface AgendaListProps {
activityId: number;
isOrganizer?: boolean;
}

@observer
export class AgendaList extends ObservedComponent<AgendaListProps, typeof i18n> {
static contextType = I18nContext;

agendaStore = new AgendaModel(this.props.activityId);

@computed
get columns(): Column<Agenda>[] {
const { t } = this.observedContext;
const { isOrganizer = false } = this.observedProps;

return [
{
renderHead: t('title'),
renderBody: ({ forum }) => forum?.title || t('unknown'),
required: true,
invalidMessage: t('field_required'),
},
{
renderHead: t('summary'),
renderBody: ({ forum }) => forum?.summary || '-',
type: 'textarea',
rows: 3,
},
{
renderHead: t('start_time'),
renderBody: ({ forum }) => forum?.startTime ? new Date(forum.startTime).toLocaleString() : '-',
type: 'datetime-local',
},
{
renderHead: t('end_time'),
renderBody: ({ forum }) => forum?.endTime ? new Date(forum.endTime).toLocaleString() : '-',
type: 'datetime-local',
},
{
renderHead: t('place'),
renderBody: ({ forum }) => forum?.place?.name || t('unknown'),
renderInput: renderTagInput(placeStore),
required: true,
invalidMessage: t('field_required'),
},
{
key: 'mentors',
renderHead: t('mentors'),
renderBody: ({ mentors }) =>
mentors?.map(mentor => mentor.name).join(', ') || '-',
renderInput: renderTagInput(userStore),
required: true,
invalidMessage: t('field_required'),
},
{
key: 'adopted',
renderHead: t('status'),
renderBody: ({ adopted }) => (
<Badge bg={adopted ? 'success' : 'warning'}>
{adopted ? t('approved') : t('pending_review')}
</Badge>
),
type: 'checkbox',
readOnly: !isOrganizer,
},
];
}

render() {
return (
<RestTable
className="h-100 text-center"
striped
hover
editable
deletable
columns={this.columns}
store={this.agendaStore}
translator={this.observedContext}
/>
);
}
}
2 changes: 2 additions & 0 deletions components/Activity/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ export const organizerMenu = ({ t }: typeof i18n, activityId: number): MenuItem[
{ href: `/activity/${activityId}/editor`, title: t('edit_activity') },
{ href: `/activity/${activityId}/forum`, title: t('forum_list') },
{ href: `/activity/${activityId}/cooperation`, title: t('cooperation_management') },
{ href: `/activity/${activityId}/agenda`, title: t('agenda_management') },
];

export const userMenu = ({ t }: typeof i18n): MenuItem[] => [
{ href: '/user/organization', title: t('organization_list') },
{ href: '/user/activity', title: t('activity_list') },
{ href: '/user/session', title: t('session_list') },
{ href: '/user/agenda', title: t('agenda_management') },
];
87 changes: 87 additions & 0 deletions components/Session/List.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { Session } from '@open-source-bazaar/activityhub-service';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import { ObservedComponent } from 'mobx-react-helper';
import { Column, RestTable } from 'mobx-restful-table';
import { Button } from 'react-bootstrap';

import sessionStore from '../../models/Session';
import { i18n, I18nContext } from '../../models/Translation';

export interface SessionListProps {
showActions?: boolean;
}

@observer
export class SessionList extends ObservedComponent<SessionListProps, typeof i18n> {
static contextType = I18nContext;

@computed
get columns(): Column<Session>[] {
const { t } = this.observedContext;
const { showActions = true } = this.observedProps;

const baseColumns: Column<Session>[] = [
{
key: 'title',
renderHead: t('title'),
renderBody: ({ title }) => title || t('unknown'),
required: true,
invalidMessage: t('field_required'),
},
{
key: 'summary',
renderHead: t('summary'),
renderBody: ({ summary }) => summary || '-',
type: 'textarea',
rows: 3,
},
{
key: 'durationMinute',
renderHead: t('duration_minutes'),
renderBody: ({ durationMinute }) => `${durationMinute || 0} ${t('minutes')}`,
type: 'number',
min: 1,
required: true,
invalidMessage: t('field_required'),
},
{
key: 'peopleCapacity',
renderHead: t('people_capacity'),
renderBody: ({ peopleCapacity }) => peopleCapacity || '-',
type: 'number',
min: 1,
},
];

if (showActions) {
baseColumns.push({
renderHead: t('actions'),
renderBody: () => (
<Button variant="outline-success" size="sm" href="/user/agenda">
{t('submit_to_activity')}
</Button>
),
});
}

return baseColumns;
}

render() {
const { showActions = true } = this.observedProps;

return (
<RestTable
className="h-100 text-center"
striped
hover
editable={showActions}
deletable={showActions}
columns={this.columns}
store={sessionStore}
translator={this.observedContext}
/>
);
}
}
14 changes: 14 additions & 0 deletions models/Agenda.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Agenda } from '@open-source-bazaar/activityhub-service';

import { TableModel } from './Base';
import userStore from './User';

export class AgendaModel extends TableModel<Agenda> {
baseURI = '';
client = userStore.client;

constructor(activityId: number) {
super();
this.baseURI = `activity/${activityId}/agenda`;
}
}
11 changes: 11 additions & 0 deletions models/Session.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Session } from '@open-source-bazaar/activityhub-service';

import { TableModel } from './Base';
import userStore from './User';

export class SessionModel extends TableModel<Session> {
baseURI = 'session';
client = userStore.client;
}

export default new SessionModel();
32 changes: 32 additions & 0 deletions pages/activity/[id]/agenda/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { User } from '@open-source-bazaar/activityhub-service';
import { observer } from 'mobx-react';
import { useRouter } from 'next/router';
import { compose, JWTProps, jwtVerifier } from 'next-ssr-middleware';
import { FC, useContext } from 'react';

import { AgendaList } from '../../../../components/Activity/AgendaList';
import { organizerMenu } from '../../../../components/Activity/menu';
import { SessionBox } from '../../../../components/User/SessionBox';
import { I18nContext } from '../../../../models/Translation';

interface AgendaListPageProps extends JWTProps<User> {}

export const getServerSideProps = compose<{ id: string }, AgendaListPageProps>(
jwtVerifier(),
);

const AgendaListPage: FC<AgendaListPageProps> = observer(({ jwtPayload }) => {
const { asPath, query } = useRouter(),
i18n = useContext(I18nContext);

const activityId = +(query.id as string);
const title = i18n.t('agenda_management');

return (
<SessionBox {...{ title, jwtPayload }} path={asPath} menu={organizerMenu(i18n, activityId)}>
<AgendaList activityId={activityId} isOrganizer />
</SessionBox>
);
});

export default AgendaListPage;
29 changes: 29 additions & 0 deletions pages/user/agenda.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { User } from '@open-source-bazaar/activityhub-service';
import { observer } from 'mobx-react';
import { useRouter } from 'next/router';
import { JWTProps, jwtVerifier } from 'next-ssr-middleware';
import { FC, useContext } from 'react';

import { AgendaList } from '../../components/Activity/AgendaList';
import { userMenu } from '../../components/Activity/menu';
import { SessionBox } from '../../components/User/SessionBox';
import { I18nContext } from '../../models/Translation';

interface UserAgendaPageProps extends JWTProps<User> {}

export const getServerSideProps = jwtVerifier<UserAgendaPageProps>();

const UserAgendaPage: FC<UserAgendaPageProps> = observer(({ jwtPayload }) => {
const { asPath } = useRouter(),
i18n = useContext(I18nContext);

const title = i18n.t('agenda_management');

return (
<SessionBox {...{ title, jwtPayload }} path={asPath} menu={userMenu(i18n)}>
<AgendaList activityId={0} />
</SessionBox>
);
});

export default UserAgendaPage;
36 changes: 36 additions & 0 deletions pages/user/session/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { User } from '@open-source-bazaar/activityhub-service';
import { observer } from 'mobx-react';
import { useRouter } from 'next/router';
import { compose, JWTProps, jwtVerifier } from 'next-ssr-middleware';
import { FC, useContext } from 'react';

import { userMenu } from '../../../components/Activity/menu';
import { SessionList } from '../../../components/Session/List';
import { SessionBox } from '../../../components/User/SessionBox';
import { I18nContext } from '../../../models/Translation';

interface SessionListPageProps extends JWTProps<User> {}

export const getServerSideProps = compose<{}, SessionListPageProps>(
jwtVerifier(),
async () => ({ props: {} }),
);

const SessionListPage: FC<SessionListPageProps> = observer(({ jwtPayload }) => {
const { asPath } = useRouter(),
i18n = useContext(I18nContext);

const title = i18n.t('session_list');

return (
<SessionBox
{...{ title, jwtPayload }}
path={asPath}
menu={userMenu(i18n)}
>
<SessionList />
</SessionBox>
);
});

export default SessionListPage;
26 changes: 26 additions & 0 deletions translation/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,5 +145,31 @@ export default {
logo: 'Logo',
activity_list: 'Activity List',
session_list: 'Session List',

// Session Management
session_management: 'Session Management',
create_session: 'Create Session',
edit_session: 'Edit Session',
session_title: 'Session Title',
session_summary: 'Session Summary',
duration_minutes: 'Duration (Minutes)',
minutes: 'minutes',
people_capacity: 'People Capacity',
session_created_successfully: 'Session created successfully!',
session_updated_successfully: 'Session updated successfully!',
submit_to_activity: 'Submit to Activity',
select_activity: 'Select Activity',
please_select: 'Please select...',
submitting: 'Submitting...',
agenda_submitted_successfully: 'Agenda submitted successfully!',
submission_failed: 'Submission failed. Please try again.',
submitted: 'Submitted',
agenda_management: 'Agenda Management',
submitted_agendas: 'Submitted Agendas',
review_agenda: 'Review Agenda',
mentors: 'Mentors',
approved: 'Approved',
pending_review: 'Pending Review',

unknown: 'Unknown',
} as const;
26 changes: 26 additions & 0 deletions translation/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,5 +143,31 @@ export default {
logo: '标志',
activity_list: '活动列表',
session_list: '环节列表',

// Session Management
session_management: '环节管理',
create_session: '创建环节',
edit_session: '编辑环节',
session_title: '环节标题',
session_summary: '环节简介',
duration_minutes: '持续时间(分钟)',
minutes: '分钟',
people_capacity: '人员容量',
session_created_successfully: '环节创建成功!',
session_updated_successfully: '环节更新成功!',
submit_to_activity: '提交到活动',
select_activity: '选择活动',
please_select: '请选择...',
submitting: '提交中...',
agenda_submitted_successfully: '议程提交成功!',
submission_failed: '提交失败,请重试。',
submitted: '已提交',
agenda_management: '议程管理',
submitted_agendas: '已提交议程',
review_agenda: '审核议程',
mentors: '导师',
approved: '已批准',
pending_review: '待审核',

unknown: '未知',
} as const;
Loading