Add reason for registration mode = "moderate" - #877
This commit is contained in:
@@ -2,8 +2,18 @@ import gql from "graphql-tag";
|
|||||||
import { ACTOR_FRAGMENT } from "./actor";
|
import { ACTOR_FRAGMENT } from "./actor";
|
||||||
|
|
||||||
export const CREATE_USER = gql`
|
export const CREATE_USER = gql`
|
||||||
mutation CreateUser($email: String!, $password: String!, $locale: String) {
|
mutation CreateUser(
|
||||||
createUser(email: $email, password: $password, locale: $locale) {
|
$email: String!
|
||||||
|
$password: String!
|
||||||
|
$moderation: String!
|
||||||
|
$locale: String
|
||||||
|
) {
|
||||||
|
createUser(
|
||||||
|
email: $email
|
||||||
|
password: $password
|
||||||
|
moderation: $moderation
|
||||||
|
locale: $locale
|
||||||
|
) {
|
||||||
email
|
email
|
||||||
confirmationSentAt
|
confirmationSentAt
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -976,6 +976,7 @@
|
|||||||
"Registration is closed.": "Registration is closed.",
|
"Registration is closed.": "Registration is closed.",
|
||||||
"Registration is currently closed.": "Registration is currently closed.",
|
"Registration is currently closed.": "Registration is currently closed.",
|
||||||
"Registration is moderated, new user must be validated.": "Registration is moderated, new user must be validated.",
|
"Registration is moderated, new user must be validated.": "Registration is moderated, new user must be validated.",
|
||||||
|
"Registration is subject to moderation, indicate your motivation.": "Registration is subject to moderation, indicate your motivation.",
|
||||||
"Registrations": "Registrations",
|
"Registrations": "Registrations",
|
||||||
"Registrations are restricted by allowlisting.": "Registrations are restricted by allowlisting.",
|
"Registrations are restricted by allowlisting.": "Registrations are restricted by allowlisting.",
|
||||||
"Reject": "Reject",
|
"Reject": "Reject",
|
||||||
|
|||||||
@@ -107,7 +107,7 @@
|
|||||||
|
|
||||||
<o-field
|
<o-field
|
||||||
:label="t('Password')"
|
:label="t('Password')"
|
||||||
:type="errorPasswordType"
|
:variant="errorPasswordType"
|
||||||
:message="errorPasswordMessage"
|
:message="errorPasswordMessage"
|
||||||
label-for="password"
|
label-for="password"
|
||||||
>
|
>
|
||||||
@@ -123,6 +123,28 @@
|
|||||||
/>
|
/>
|
||||||
</o-field>
|
</o-field>
|
||||||
|
|
||||||
|
<o-field
|
||||||
|
v-if="config?.registrationsModeration"
|
||||||
|
:label="
|
||||||
|
t(
|
||||||
|
'Registration is subject to moderation, indicate your motivation.'
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:variant="errorModerationType"
|
||||||
|
:message="errorModerationMessage"
|
||||||
|
label-for="moderation"
|
||||||
|
>
|
||||||
|
<o-input
|
||||||
|
aria-required="true"
|
||||||
|
required
|
||||||
|
:autosize="false"
|
||||||
|
id="moderation"
|
||||||
|
type="textarea"
|
||||||
|
v-model="credentials.moderation"
|
||||||
|
expanded
|
||||||
|
/>
|
||||||
|
</o-field>
|
||||||
|
|
||||||
<div class="flex items-start mb-6 mt-2">
|
<div class="flex items-start mb-6 mt-2">
|
||||||
<div class="flex items-center h-5">
|
<div class="flex items-center h-5">
|
||||||
<input
|
<input
|
||||||
@@ -184,6 +206,7 @@
|
|||||||
query: {
|
query: {
|
||||||
email: credentials.email,
|
email: credentials.email,
|
||||||
password: credentials.password,
|
password: credentials.password,
|
||||||
|
moderation: credentials.moderation,
|
||||||
},
|
},
|
||||||
}"
|
}"
|
||||||
>{{ t("Login") }}</o-button
|
>{{ t("Login") }}</o-button
|
||||||
@@ -241,7 +264,12 @@ import { AbsintheGraphQLErrors } from "@/types/errors.model";
|
|||||||
|
|
||||||
type errorType = "danger" | "warning";
|
type errorType = "danger" | "warning";
|
||||||
type errorMessage = { type: errorType; message: string };
|
type errorMessage = { type: errorType; message: string };
|
||||||
type credentialsType = { email: string; password: string; locale: string };
|
type credentialsType = {
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
moderation: string;
|
||||||
|
locale: string;
|
||||||
|
};
|
||||||
|
|
||||||
const { t, locale } = useI18n({ useScope: "global" });
|
const { t, locale } = useI18n({ useScope: "global" });
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
@@ -255,11 +283,14 @@ const credentials = reactive<credentialsType>({
|
|||||||
email: typeof route.query.email === "string" ? route.query.email : "",
|
email: typeof route.query.email === "string" ? route.query.email : "",
|
||||||
password:
|
password:
|
||||||
typeof route.query.password === "string" ? route.query.password : "",
|
typeof route.query.password === "string" ? route.query.password : "",
|
||||||
|
moderation:
|
||||||
|
typeof route.query.moderation === "string" ? route.query.moderation : "",
|
||||||
locale: "en",
|
locale: "en",
|
||||||
});
|
});
|
||||||
|
|
||||||
const emailErrors = ref<errorMessage[]>([]);
|
const emailErrors = ref<errorMessage[]>([]);
|
||||||
const passwordErrors = ref<errorMessage[]>([]);
|
const passwordErrors = ref<errorMessage[]>([]);
|
||||||
|
const moderationError = ref<errorMessage[]>([]);
|
||||||
|
|
||||||
const sendingForm = ref(false);
|
const sendingForm = ref(false);
|
||||||
|
|
||||||
@@ -298,6 +329,12 @@ onError((error) => {
|
|||||||
message: message[0] as string,
|
message: message[0] as string,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case "moderation":
|
||||||
|
moderationError.value.push({
|
||||||
|
type: "danger" as errorType,
|
||||||
|
message: message[0] as string,
|
||||||
|
});
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,6 +348,7 @@ const submit = async (): Promise<void> => {
|
|||||||
try {
|
try {
|
||||||
emailErrors.value = [];
|
emailErrors.value = [];
|
||||||
passwordErrors.value = [];
|
passwordErrors.value = [];
|
||||||
|
moderationError.value = [];
|
||||||
|
|
||||||
mutate(credentials);
|
mutate(credentials);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -343,11 +381,14 @@ const maxErrorType = (errors: errorMessage[]): errorType | undefined => {
|
|||||||
const errorEmailType = computed((): errorType | undefined => {
|
const errorEmailType = computed((): errorType | undefined => {
|
||||||
return maxErrorType(emailErrors.value);
|
return maxErrorType(emailErrors.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const errorPasswordType = computed((): errorType | undefined => {
|
const errorPasswordType = computed((): errorType | undefined => {
|
||||||
return maxErrorType(passwordErrors.value);
|
return maxErrorType(passwordErrors.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const errorModerationType = computed((): errorType | undefined => {
|
||||||
|
return maxErrorType(moderationError.value);
|
||||||
|
});
|
||||||
|
|
||||||
const errorEmailMessage = computed((): string => {
|
const errorEmailMessage = computed((): string => {
|
||||||
return emailErrors.value.map(({ message }) => message).join(" ");
|
return emailErrors.value.map(({ message }) => message).join(" ");
|
||||||
});
|
});
|
||||||
@@ -355,4 +396,8 @@ const errorEmailMessage = computed((): string => {
|
|||||||
const errorPasswordMessage = computed((): string => {
|
const errorPasswordMessage = computed((): string => {
|
||||||
return passwordErrors.value?.map(({ message }) => message).join(" ");
|
return passwordErrors.value?.map(({ message }) => message).join(" ");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const errorModerationMessage = computed((): string => {
|
||||||
|
return moderationError.value?.map(({ message }) => message).join(" ");
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
185
tests/unit/specs/components/User/RegisterView.spec.ts
Normal file
185
tests/unit/specs/components/User/RegisterView.spec.ts
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
import { config, mount } from "@vue/test-utils";
|
||||||
|
import RegisterView from "@/views/User/RegisterView.vue";
|
||||||
|
import { createMockClient, RequestHandler } from "mock-apollo-client";
|
||||||
|
import flushPromises from "flush-promises";
|
||||||
|
import { configMock } from "../../mocks/config";
|
||||||
|
import { CONFIG } from "@/graphql/config";
|
||||||
|
import { CREATE_USER } from "@/graphql/user";
|
||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { DefaultApolloClient } from "@vue/apollo-composable";
|
||||||
|
import Oruga from "@oruga-ui/oruga-next";
|
||||||
|
import {
|
||||||
|
VueRouterMock,
|
||||||
|
createRouterMock,
|
||||||
|
injectRouterMock,
|
||||||
|
} from "vue-router-mock";
|
||||||
|
import { nullMock } from "../../common";
|
||||||
|
|
||||||
|
config.global.plugins.push(Oruga);
|
||||||
|
config.plugins.VueWrapper.install(VueRouterMock);
|
||||||
|
|
||||||
|
let requestHandlers: Record<string, RequestHandler>;
|
||||||
|
|
||||||
|
const generateWrapper = (
|
||||||
|
customRegModeration: boolean = false,
|
||||||
|
customRequestHandlers: Record<string, RequestHandler> = {}
|
||||||
|
) => {
|
||||||
|
const mockClient = createMockClient();
|
||||||
|
|
||||||
|
const config_value = {
|
||||||
|
...configMock,
|
||||||
|
};
|
||||||
|
if (customRegModeration) {
|
||||||
|
config_value.data.config.registrationsModeration = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestHandlers = {
|
||||||
|
configQueryHandler: vi.fn().mockResolvedValue(config_value),
|
||||||
|
createUserHandler: vi.fn().mockResolvedValue(nullMock),
|
||||||
|
...customRequestHandlers,
|
||||||
|
};
|
||||||
|
|
||||||
|
mockClient.setRequestHandler(CONFIG, requestHandlers.configQueryHandler);
|
||||||
|
mockClient.setRequestHandler(CREATE_USER, requestHandlers.createUserHandler);
|
||||||
|
|
||||||
|
return mount(RegisterView, {
|
||||||
|
global: {
|
||||||
|
stubs: ["router-link", "router-view"],
|
||||||
|
provide: {
|
||||||
|
[DefaultApolloClient]: mockClient,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Register page", () => {
|
||||||
|
const router = createRouterMock({
|
||||||
|
spy: {
|
||||||
|
create: (fn) => vi.fn(fn),
|
||||||
|
reset: (spy) => spy.mockReset(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
beforeEach(() => {
|
||||||
|
// inject it globally to ensure `useRoute()`, `$route`, etc work
|
||||||
|
// properly and give you access to test specific functions
|
||||||
|
injectRouterMock(router);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("register without moderation", async () => {
|
||||||
|
const wrapper = generateWrapper();
|
||||||
|
expect(wrapper.router).toBe(router);
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
expect(wrapper.find("form").exists()).toBe(true);
|
||||||
|
wrapper.find('form input[type="email"]').setValue("some@email.tld");
|
||||||
|
wrapper.find('form input[type="password"]').setValue("somepassword");
|
||||||
|
wrapper.find("form").trigger("submit");
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
expect(requestHandlers.createUserHandler).toHaveBeenCalledWith({
|
||||||
|
email: "some@email.tld",
|
||||||
|
locale: "en_US",
|
||||||
|
moderation: "",
|
||||||
|
password: "somepassword",
|
||||||
|
});
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.find("form").exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error without moderation email", async () => {
|
||||||
|
const wrapper = generateWrapper(false, {
|
||||||
|
createUserHandler: vi.fn().mockResolvedValue({
|
||||||
|
errors: [{ field: "email", message: ["Bad email."] }],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(wrapper.find("form").exists()).toBe(true);
|
||||||
|
wrapper
|
||||||
|
.findAll('input[type="password"')
|
||||||
|
.forEach((inputField) => inputField.setValue("my password"));
|
||||||
|
wrapper.find("form").trigger("submit");
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
expect(requestHandlers.createUserHandler).toBeCalledTimes(1);
|
||||||
|
expect(requestHandlers.createUserHandler).toHaveBeenCalledWith({
|
||||||
|
email: "",
|
||||||
|
locale: "en_US",
|
||||||
|
moderation: "",
|
||||||
|
password: "my password",
|
||||||
|
});
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.find("form").exists()).toBe(true);
|
||||||
|
expect(wrapper.find(".o-field__message-danger").text()).toContain(
|
||||||
|
"Bad email."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error without moderation password", async () => {
|
||||||
|
const wrapper = generateWrapper(false, {
|
||||||
|
createUserHandler: vi.fn().mockResolvedValue({
|
||||||
|
errors: [{ field: "password", message: ["Bad password."] }],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(wrapper.find("form").exists()).toBe(true);
|
||||||
|
wrapper
|
||||||
|
.findAll('input[type="password"')
|
||||||
|
.forEach((inputField) => inputField.setValue("my password"));
|
||||||
|
wrapper.find("form").trigger("submit");
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
expect(requestHandlers.createUserHandler).toBeCalledTimes(1);
|
||||||
|
expect(requestHandlers.createUserHandler).toHaveBeenCalledWith({
|
||||||
|
email: "",
|
||||||
|
locale: "en_US",
|
||||||
|
moderation: "",
|
||||||
|
password: "my password",
|
||||||
|
});
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.find("form").exists()).toBe(true);
|
||||||
|
expect(wrapper.find(".o-field__message-danger").text()).toContain(
|
||||||
|
"Bad password."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("register with moderation", async () => {
|
||||||
|
const wrapper = generateWrapper(true);
|
||||||
|
expect(wrapper.router).toBe(router);
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.html()).toMatchSnapshot();
|
||||||
|
expect(wrapper.find("form").exists()).toBe(true);
|
||||||
|
wrapper.find('form input[type="email"]').setValue("some@email.tld");
|
||||||
|
wrapper.find('form input[type="password"]').setValue("somepassword");
|
||||||
|
wrapper.find("form").trigger("submit");
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
expect(requestHandlers.createUserHandler).toHaveBeenCalledWith({
|
||||||
|
email: "some@email.tld",
|
||||||
|
locale: "en_US",
|
||||||
|
moderation: "",
|
||||||
|
password: "somepassword",
|
||||||
|
});
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.find("form").exists()).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows error with moderation", async () => {
|
||||||
|
const wrapper = generateWrapper(true, {
|
||||||
|
createUserHandler: vi.fn().mockResolvedValue({
|
||||||
|
errors: [{ field: "moderation", message: ["Bad moderation."] }],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
expect(wrapper.find("form").exists()).toBe(true);
|
||||||
|
wrapper
|
||||||
|
.findAll('input[type="password"')
|
||||||
|
.forEach((inputField) => inputField.setValue("my password"));
|
||||||
|
wrapper.find("form").trigger("submit");
|
||||||
|
await wrapper.vm.$nextTick();
|
||||||
|
expect(requestHandlers.createUserHandler).toBeCalledTimes(1);
|
||||||
|
expect(requestHandlers.createUserHandler).toHaveBeenCalledWith({
|
||||||
|
email: "",
|
||||||
|
locale: "en_US",
|
||||||
|
moderation: "",
|
||||||
|
password: "my password",
|
||||||
|
});
|
||||||
|
await flushPromises();
|
||||||
|
expect(wrapper.find("form").exists()).toBe(true);
|
||||||
|
expect(wrapper.find(".o-field__message-danger").text()).toContain(
|
||||||
|
"Bad moderation."
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`Register page > register with moderation 1`] = `
|
||||||
|
"<div class="container mx-auto py-6">
|
||||||
|
<section class="">
|
||||||
|
<h1>Register an account on Mobilizon!</h1>
|
||||||
|
<p><b>Mobilizon</b> is an instance of the <a href="https://mobilizon.org" target="_blank" class="out">Mobilizon</a> software.</p>
|
||||||
|
</section>
|
||||||
|
<section class="flex flex-wrap gap-6">
|
||||||
|
<div class="">
|
||||||
|
<div class="my-4">
|
||||||
|
<h2 class="text-xl">Why create an account?</h2>
|
||||||
|
<div class="prose dark:prose-invert">
|
||||||
|
<ul>
|
||||||
|
<li>To create and manage your events</li>
|
||||||
|
<li>To create and manage multiples identities from a same account</li>
|
||||||
|
<li>To register for an event by choosing one of your identities</li>
|
||||||
|
<li>To create or join an group and start organizing with other people</li>
|
||||||
|
<li>To follow groups and be informed of their latest events</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<router-link-stub to="[object Object]" replace="false" custom="false" ariacurrentvalue="page" viewtransition="false" class="out block my-4"></router-link-stub>
|
||||||
|
<hr role="presentation">
|
||||||
|
<div class="my-4">
|
||||||
|
<h2 class="text-xl">About Mobilizon</h2>
|
||||||
|
<div class="prose dark:prose-invert">Mobilizon.fr est l'instance Mobilizon de Framasoft.</div>
|
||||||
|
<p>Please read the <router-link-stub to="[object Object]" replace="false" custom="false" ariacurrentvalue="page" viewtransition="false" class="out"></router-link-stub> published by <b>Mobilizon</b>'s administrators.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<!--v-if-->
|
||||||
|
<form>
|
||||||
|
<div data-oruga="field" class="o-field"><label for="email" class="o-field__label">Email</label>
|
||||||
|
<div class="o-field__body">
|
||||||
|
<div class="o-field o-field--addons">
|
||||||
|
<div data-oruga="input" class="o-input__wrapper o-input__wrapper--expanded"><input aria-required="true" required="" id="email" data-oruga-input="email" type="email" class="o-input" autocomplete="off">
|
||||||
|
<!---->
|
||||||
|
<!---->
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
<div data-oruga="field" class="o-field"><label for="password" class="o-field__label">Password</label>
|
||||||
|
<div class="o-field__body">
|
||||||
|
<div class="o-field o-field--addons">
|
||||||
|
<div data-oruga="input" class="o-input__wrapper o-input__wrapper--expanded o-input__wrapper--has-icon-right"><input aria-required="true" required="" minlength="6" id="password" data-oruga-input="password" type="password" class="o-input o-input--iconspace-right" autocomplete="off">
|
||||||
|
<!----><span class="o-icon o-icon--clickable o-input__icon-right" data-oruga="icon"><i class="mdi mdi-eye mdi-24px"></i></span>
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
<div data-oruga="field" class="o-field"><label for="moderation" class="o-field__label">Registration is subject to moderation, indicate your motivation.</label>
|
||||||
|
<div class="o-field__body">
|
||||||
|
<div class="o-field o-field--addons">
|
||||||
|
<div data-oruga="input" class="o-input__wrapper o-input__wrapper--expanded"><textarea aria-required="true" required="" id="moderation" data-oruga-input="textarea" class="o-input o-input__textarea"></textarea>
|
||||||
|
<!---->
|
||||||
|
<!---->
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
<div class="flex items-start mb-6 mt-2">
|
||||||
|
<div class="flex items-center h-5"><input type="checkbox" id="accept_rules_terms" class="w-4 h-4 bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" required=""></div><label for="accept_rules_terms" class="ml-2 text-gray-900 dark:text-gray-300"><span>I agree to the <router-link-stub to="[object Object]" replace="false" custom="false" ariacurrentvalue="page" viewtransition="false" class="out"></router-link-stub> and <router-link-stub to="[object Object]" replace="false" custom="false" ariacurrentvalue="page" viewtransition="false" class="out"></router-link-stub></span></label>
|
||||||
|
</div>
|
||||||
|
<p><button type="submit" class="o-btn o-btn--large o-btn--primary" role="button" data-oruga="button"><span class="o-btn__wrapper"><!----><span class="o-btn__label">Create an account</span>
|
||||||
|
<!----></span>
|
||||||
|
</button></p>
|
||||||
|
<p class="my-6">
|
||||||
|
<router-link-stub class="o-btn o-btn--text" role="button" data-oruga="button" to="[object Object]"></router-link-stub>
|
||||||
|
<router-link-stub class="o-btn o-btn--text" role="button" data-oruga="button" to="[object Object]"></router-link-stub>
|
||||||
|
</p>
|
||||||
|
<hr role="presentation">
|
||||||
|
<!--v-if-->
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Register page > register without moderation 1`] = `
|
||||||
|
"<div class="container mx-auto py-6">
|
||||||
|
<section class="">
|
||||||
|
<h1>Register an account on Mobilizon!</h1>
|
||||||
|
<p><b>Mobilizon</b> is an instance of the <a href="https://mobilizon.org" target="_blank" class="out">Mobilizon</a> software.</p>
|
||||||
|
</section>
|
||||||
|
<section class="flex flex-wrap gap-6">
|
||||||
|
<div class="">
|
||||||
|
<div class="my-4">
|
||||||
|
<h2 class="text-xl">Why create an account?</h2>
|
||||||
|
<div class="prose dark:prose-invert">
|
||||||
|
<ul>
|
||||||
|
<li>To create and manage your events</li>
|
||||||
|
<li>To create and manage multiples identities from a same account</li>
|
||||||
|
<li>To register for an event by choosing one of your identities</li>
|
||||||
|
<li>To create or join an group and start organizing with other people</li>
|
||||||
|
<li>To follow groups and be informed of their latest events</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<router-link-stub to="[object Object]" replace="false" custom="false" ariacurrentvalue="page" viewtransition="false" class="out block my-4"></router-link-stub>
|
||||||
|
<hr role="presentation">
|
||||||
|
<div class="my-4">
|
||||||
|
<h2 class="text-xl">About Mobilizon</h2>
|
||||||
|
<div class="prose dark:prose-invert">Mobilizon.fr est l'instance Mobilizon de Framasoft.</div>
|
||||||
|
<p>Please read the <router-link-stub to="[object Object]" replace="false" custom="false" ariacurrentvalue="page" viewtransition="false" class="out"></router-link-stub> published by <b>Mobilizon</b>'s administrators.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<!--v-if-->
|
||||||
|
<form>
|
||||||
|
<div data-oruga="field" class="o-field"><label for="email" class="o-field__label">Email</label>
|
||||||
|
<div class="o-field__body">
|
||||||
|
<div class="o-field o-field--addons">
|
||||||
|
<div data-oruga="input" class="o-input__wrapper o-input__wrapper--expanded"><input aria-required="true" required="" id="email" data-oruga-input="email" type="email" class="o-input" autocomplete="off">
|
||||||
|
<!---->
|
||||||
|
<!---->
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
<div data-oruga="field" class="o-field"><label for="password" class="o-field__label">Password</label>
|
||||||
|
<div class="o-field__body">
|
||||||
|
<div class="o-field o-field--addons">
|
||||||
|
<div data-oruga="input" class="o-input__wrapper o-input__wrapper--expanded o-input__wrapper--has-icon-right"><input aria-required="true" required="" minlength="6" id="password" data-oruga-input="password" type="password" class="o-input o-input--iconspace-right" autocomplete="off">
|
||||||
|
<!----><span class="o-icon o-icon--clickable o-input__icon-right" data-oruga="icon"><i class="mdi mdi-eye mdi-24px"></i></span>
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!---->
|
||||||
|
</div>
|
||||||
|
<!--v-if-->
|
||||||
|
<div class="flex items-start mb-6 mt-2">
|
||||||
|
<div class="flex items-center h-5"><input type="checkbox" id="accept_rules_terms" class="w-4 h-4 bg-gray-50 rounded border border-gray-300 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800" required=""></div><label for="accept_rules_terms" class="ml-2 text-gray-900 dark:text-gray-300"><span>I agree to the <router-link-stub to="[object Object]" replace="false" custom="false" ariacurrentvalue="page" viewtransition="false" class="out"></router-link-stub> and <router-link-stub to="[object Object]" replace="false" custom="false" ariacurrentvalue="page" viewtransition="false" class="out"></router-link-stub></span></label>
|
||||||
|
</div>
|
||||||
|
<p><button type="submit" class="o-btn o-btn--large o-btn--primary" role="button" data-oruga="button"><span class="o-btn__wrapper"><!----><span class="o-btn__label">Create an account</span>
|
||||||
|
<!----></span>
|
||||||
|
</button></p>
|
||||||
|
<p class="my-6">
|
||||||
|
<router-link-stub class="o-btn o-btn--text" role="button" data-oruga="button" to="[object Object]"></router-link-stub>
|
||||||
|
<router-link-stub class="o-btn o-btn--text" role="button" data-oruga="button" to="[object Object]"></router-link-stub>
|
||||||
|
</p>
|
||||||
|
<hr role="presentation">
|
||||||
|
<!--v-if-->
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>"
|
||||||
|
`;
|
||||||
Reference in New Issue
Block a user