mirror of
https://github.com/umami-software/umami.git
synced 2026-02-19 12:05:41 +01:00
feat: add invitation and join team features
This commit is contained in:
parent
60eaaaff60
commit
c61188d524
8 changed files with 110 additions and 0 deletions
22
src/app/(main)/invite/InviteForm.module.css
Normal file
22
src/app/(main)/invite/InviteForm.module.css
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
.container {
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 400px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
background: var(--base50);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.container {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/app/(main)/invite/InviteForm.tsx
Normal file
55
src/app/(main)/invite/InviteForm.tsx
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
'use client';
|
||||||
|
import { Form, FormRow, FormInput, FormButtons, TextField, Button } from 'react-basics';
|
||||||
|
import { useApi, useMessages, useModified } from '@/components/hooks';
|
||||||
|
import styles from './InviteForm.module.css';
|
||||||
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
|
export function InviteForm() {
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
|
||||||
|
const accessCode = searchParams.get('accessCode');
|
||||||
|
const { formatMessage, labels } = useMessages();
|
||||||
|
const { post, useMutation } = useApi();
|
||||||
|
|
||||||
|
const { mutate, error } = useMutation({
|
||||||
|
mutationFn: (data: any) => post('/teams/join', data),
|
||||||
|
});
|
||||||
|
|
||||||
|
const { touch } = useModified();
|
||||||
|
|
||||||
|
const handleSubmit = async (data: any) => {
|
||||||
|
mutate(data, {
|
||||||
|
onSuccess: async () => {
|
||||||
|
touch('teams:members');
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.container}>
|
||||||
|
<div className={styles.form}>
|
||||||
|
<Form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
error={error}
|
||||||
|
values={{
|
||||||
|
accessCode: accessCode || '',
|
||||||
|
}}
|
||||||
|
autoComplete="off"
|
||||||
|
preventSubmit={false}
|
||||||
|
>
|
||||||
|
<FormRow label={formatMessage(labels.accessCode)}>
|
||||||
|
<FormInput name="accessCode" rules={{ required: formatMessage(labels.required) }}>
|
||||||
|
<TextField autoComplete="off" />
|
||||||
|
</FormInput>
|
||||||
|
</FormRow>
|
||||||
|
<FormButtons flex>
|
||||||
|
<Button type="submit" variant="primary">
|
||||||
|
{formatMessage(labels.join)}
|
||||||
|
</Button>
|
||||||
|
<Button onClick={() => {}}>{formatMessage(labels.cancel)}</Button>
|
||||||
|
</FormButtons>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
11
src/app/(main)/invite/InvitePage.tsx
Normal file
11
src/app/(main)/invite/InvitePage.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { InviteForm } from './InviteForm';
|
||||||
|
|
||||||
|
export default function InvitePage() {
|
||||||
|
return (
|
||||||
|
<section style={{ marginBottom: 60 }}>
|
||||||
|
<InviteForm />
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
10
src/app/(main)/invite/page.tsx
Normal file
10
src/app/(main)/invite/page.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Metadata } from 'next';
|
||||||
|
import InvitePage from './InvitePage';
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Join team',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
return <InvitePage />;
|
||||||
|
}
|
||||||
|
|
@ -71,6 +71,15 @@ export function TeamEditForm({ teamId, allowEdit }: { teamId: string; allowEdit?
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
)}
|
)}
|
||||||
|
<FormRow label={formatMessage(labels.invitationLink)}>
|
||||||
|
<Flexbox gap={10}>
|
||||||
|
<TextField
|
||||||
|
value={`${window.location.host}/invite?accessCode=${accessCode}`}
|
||||||
|
readOnly
|
||||||
|
allowCopy
|
||||||
|
/>
|
||||||
|
</Flexbox>
|
||||||
|
</FormRow>
|
||||||
{allowEdit && (
|
{allowEdit && (
|
||||||
<FormButtons>
|
<FormButtons>
|
||||||
<SubmitButton variant="primary">{formatMessage(labels.save)}</SubmitButton>
|
<SubmitButton variant="primary">{formatMessage(labels.save)}</SubmitButton>
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ export const labels = defineMessages({
|
||||||
member: { id: 'label.member', defaultMessage: 'Member' },
|
member: { id: 'label.member', defaultMessage: 'Member' },
|
||||||
members: { id: 'label.members', defaultMessage: 'Members' },
|
members: { id: 'label.members', defaultMessage: 'Members' },
|
||||||
accessCode: { id: 'label.access-code', defaultMessage: 'Access code' },
|
accessCode: { id: 'label.access-code', defaultMessage: 'Access code' },
|
||||||
|
invitationLink: { id: 'label.invitation-link', defaultMessage: 'Invitation link' },
|
||||||
teamId: { id: 'label.team-id', defaultMessage: 'Team ID' },
|
teamId: { id: 'label.team-id', defaultMessage: 'Team ID' },
|
||||||
team: { id: 'label.team', defaultMessage: 'Team' },
|
team: { id: 'label.team', defaultMessage: 'Team' },
|
||||||
teamName: { id: 'label.team-name', defaultMessage: 'Team name' },
|
teamName: { id: 'label.team-name', defaultMessage: 'Team name' },
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,7 @@
|
||||||
"label.websites": "Websites",
|
"label.websites": "Websites",
|
||||||
"label.window": "Window",
|
"label.window": "Window",
|
||||||
"label.yesterday": "Yesterday",
|
"label.yesterday": "Yesterday",
|
||||||
|
"label.invitation-link": "邀请链接",
|
||||||
"message.action-confirmation": "Type {confirmation} in the box below to confirm.",
|
"message.action-confirmation": "Type {confirmation} in the box below to confirm.",
|
||||||
"message.active-users": "{x} current {x, plural, one {visitor} other {visitors}}",
|
"message.active-users": "{x} current {x, plural, one {visitor} other {visitors}}",
|
||||||
"message.collected-data": "Collected data",
|
"message.collected-data": "Collected data",
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,7 @@
|
||||||
"label.websites": "网站",
|
"label.websites": "网站",
|
||||||
"label.window": "窗口",
|
"label.window": "窗口",
|
||||||
"label.yesterday": "昨天",
|
"label.yesterday": "昨天",
|
||||||
|
"label.invitation-link": "邀请链接",
|
||||||
"message.action-confirmation": "请在下方输入框中输入 {confirmation} 以确认操作。",
|
"message.action-confirmation": "请在下方输入框中输入 {confirmation} 以确认操作。",
|
||||||
"message.active-users": "当前在线 {x} 位访客",
|
"message.active-users": "当前在线 {x} 位访客",
|
||||||
"message.collected-data": "已收集的数据",
|
"message.collected-data": "已收集的数据",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue