123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- <template>
- <div class="admin-invite-component">
- <div class="admin-invite-component-inner">
- <div class="card bg-dark">
- <div v-if="tabIndex === 0" class="card-body d-flex align-items-center justify-content-center">
- <div class="text-center">
- <b-spinner variant="muted" />
- <p class="text-muted mb-0">Loading...</p>
- </div>
- </div>
- <div v-else-if="tabIndex === 1" class="card-body">
- <div class="d-flex justify-content-center my-3">
- <img src="/img/pixelfed-icon-color.png" width="60" alt="Pixelfed logo" />
- </div>
- <div class="d-flex flex-column align-items-center justify-content-center">
- <p class="lead mb-1 text-muted">You've been invited to join</p>
- <p class="h3 mb-2">{{ instance.uri }}</p>
- <p class="mb-0 text-muted">
- <span>{{ instance.stats.user_count.toLocaleString('en-CA', { compactDisplay: "short", notation: "compact"}) }} users</span>
- <span>·</span>
- <span>{{ instance.stats.status_count.toLocaleString('en-CA', { compactDisplay: "short", notation: "compact"}) }} posts</span>
- </p>
- <div v-if="inviteConfig.message != 'You\'ve been invited to join'">
- <div class="admin-message">
- <p class="small text-light mb-0">Message from admin(s):</p>
- {{ inviteConfig.message }}
- </div>
- </div>
- </div>
- <div class="mt-5">
- <div class="form-group">
- <label for="username">Username</label>
- <input
- type="text"
- class="form-control form-control-lg"
- placeholder="What should everyone call you?"
- minlength="2"
- maxlength="15"
- v-model="form.username" />
- <p v-if="errors.username" class="form-text text-danger">
- <i class="far fa-exclamation-triangle mr-1"></i>
- {{ errors.username }}
- </p>
- </div>
- <button
- class="btn btn-primary btn-block font-weight-bold"
- @click="proceed(tabIndex)"
- :disabled="isProceeding || !form.username || form.username.length < 2">
- <template v-if="isProceeding">
- <b-spinner small />
- </template>
- <template v-else>
- Continue
- </template>
- </button>
- <p class="login-link">
- <a href="/login">Already have an account?</a>
- </p>
- <p class="register-terms">
- By registering, you agree to our <a href="/site/terms">Terms of Service</a> and <a href="/site/privacy">Privacy Policy</a>.
- </p>
- </div>
- </div>
- <div v-else-if="tabIndex === 2" class="card-body">
- <div class="d-flex justify-content-center my-3">
- <img src="/img/pixelfed-icon-color.png" width="60" alt="Pixelfed logo" />
- </div>
- <div class="d-flex flex-column align-items-center justify-content-center">
- <p class="lead mb-1 text-muted">You've been invited to join</p>
- <p class="h3 mb-2">{{ instance.uri }}</p>
- </div>
- <div class="mt-5">
- <div class="form-group">
- <label for="username">Email Address</label>
- <input
- type="email"
- class="form-control form-control-lg"
- placeholder="Your email address"
- v-model="form.email" />
- <p v-if="errors.email" class="form-text text-danger">
- <i class="far fa-exclamation-triangle mr-1"></i>
- {{ errors.email }}
- </p>
- </div>
- <button
- class="btn btn-primary btn-block font-weight-bold"
- @click="proceed(tabIndex)"
- :disabled="isProceeding || !form.email || !validateEmail()">
- <template v-if="isProceeding">
- <b-spinner small />
- </template>
- <template v-else>
- Continue
- </template>
- </button>
- </div>
- </div>
- <div v-else-if="tabIndex === 3" class="card-body">
- <div class="d-flex justify-content-center my-3">
- <img src="/img/pixelfed-icon-color.png" width="60" alt="Pixelfed logo" />
- </div>
- <div class="d-flex flex-column align-items-center justify-content-center">
- <p class="lead mb-1 text-muted">You've been invited to join</p>
- <p class="h3 mb-2">{{ instance.uri }}</p>
- </div>
- <div class="mt-5">
- <div class="form-group">
- <label for="username">Password</label>
- <input
- type="password"
- class="form-control form-control-lg"
- placeholder="Use a secure password"
- minlength="8"
- v-model="form.password" />
- <p v-if="errors.password" class="form-text text-danger">
- <i class="far fa-exclamation-triangle mr-1"></i>
- {{ errors.password }}
- </p>
- </div>
- <button
- class="btn btn-primary btn-block font-weight-bold"
- @click="proceed(tabIndex)"
- :disabled="isProceeding || !form.password || form.password.length < 8">
- <template v-if="isProceeding">
- <b-spinner small />
- </template>
- <template v-else>
- Continue
- </template>
- </button>
- </div>
- </div>
- <div v-else-if="tabIndex === 4" class="card-body">
- <div class="d-flex justify-content-center my-3">
- <img src="/img/pixelfed-icon-color.png" width="60" alt="Pixelfed logo" />
- </div>
- <div class="d-flex flex-column align-items-center justify-content-center">
- <p class="lead mb-1 text-muted">You've been invited to join</p>
- <p class="h3 mb-2">{{ instance.uri }}</p>
- </div>
- <div class="mt-5">
- <div class="form-group">
- <label for="username">Confirm Password</label>
- <input
- type="password"
- class="form-control form-control-lg"
- placeholder="Use a secure password"
- minlength="8"
- v-model="form.password_confirm" />
- <p v-if="errors.password_confirm" class="form-text text-danger">
- <i class="far fa-exclamation-triangle mr-1"></i>
- {{ errors.password_confirm }}
- </p>
- </div>
- <button
- class="btn btn-primary btn-block font-weight-bold"
- @click="proceed(tabIndex)"
- :disabled="isProceeding || !form.password_confirm || form.password !== form.password_confirm">
- <template v-if="isProceeding">
- <b-spinner small />
- </template>
- <template v-else>
- Continue
- </template>
- </button>
- </div>
- </div>
- <div v-else-if="tabIndex === 5" class="card-body">
- <div class="d-flex justify-content-center my-3">
- <img src="/img/pixelfed-icon-color.png" width="60" alt="Pixelfed logo" />
- </div>
- <div class="d-flex flex-column align-items-center justify-content-center">
- <p class="lead mb-1 text-muted">You've been invited to join</p>
- <p class="h3 mb-2">{{ instance.uri }}</p>
- </div>
- <div class="mt-5">
- <div class="form-group">
- <label for="username">Display Name</label>
- <input
- type="text"
- class="form-control form-control-lg"
- placeholder="Add an optional display name"
- minlength="8"
- v-model="form.display_name" />
- <p v-if="errors.display_name" class="form-text text-danger">
- <i class="far fa-exclamation-triangle mr-1"></i>
- {{ errors.display_name }}
- </p>
- </div>
- <button
- class="btn btn-primary btn-block font-weight-bold"
- @click="proceed(tabIndex)"
- :disabled="isProceeding">
- <template v-if="isProceeding">
- <b-spinner small />
- </template>
- <template v-else>
- Continue
- </template>
- </button>
- </div>
- </div>
- <div v-else-if="tabIndex === 6" class="card-body d-flex flex-column">
- <div class="d-flex justify-content-center my-3">
- <img src="/img/pixelfed-icon-color.png" width="60" alt="Pixelfed logo" />
- </div>
- <div class="d-flex flex-column align-items-center justify-content-center">
- <p class="lead mb-1 text-muted">You've been invited to join</p>
- <p class="h3 mb-2">{{ instance.uri }}</p>
- </div>
- <div class="mt-5 d-flex align-items-center justify-content-center flex-column flex-grow-1">
- <b-spinner variant="muted" />
- <p class="text-muted">Registering...</p>
- </div>
- </div>
- <div v-else-if="tabIndex === 'invalid-code'" class="card-body d-flex align-items-center justify-content-center">
- <div>
- <h1 class="text-center">Invalid Invite Code</h1>
- <hr>
- <p class="text-muted mb-1">The invite code you were provided is not valid, this can happen when:</p>
- <ul class="text-muted">
- <li>Invite code has typos</li>
- <li>Invite code was already used</li>
- <li>Invite code has reached max uses</li>
- <li>Invite code has expired</li>
- <li>You have been rate limited</li>
- </ul>
- <hr>
- <a href="/" class="btn btn-primary btn-block rounded-pill font-weight-bold">Go back home</a>
- </div>
- </div>
- <div v-else class="card-body">
- <p>An error occured.</p>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script type="text/javascript">
- export default {
- props: ['code'],
- data() {
- return {
- instance: {},
- inviteConfig: {},
- tabIndex: 0,
- isProceeding: false,
- errors: {
- username: undefined,
- email: undefined,
- password: undefined,
- password_confirm: undefined
- },
- form: {
- username: undefined,
- email: undefined,
- password: undefined,
- password_confirm: undefined,
- display_name: undefined,
- }
- }
- },
- mounted() {
- this.fetchInstanceData();
- },
- methods: {
- fetchInstanceData() {
- axios.get('/api/v1/instance')
- .then(res => {
- this.instance = res.data;
- })
- .then(res => {
- this.verifyToken();
- })
- .catch(err => {
- console.log(err);
- })
- },
- verifyToken() {
- axios.post('/api/v1.1/auth/invite/admin/verify', {
- token: this.code,
- })
- .then(res => {
- this.tabIndex = 1;
- this.inviteConfig = res.data;
- })
- .catch(err => {
- this.tabIndex = 'invalid-code';
- })
- },
- checkUsernameAvailability() {
- axios.post('/api/v1.1/auth/invite/admin/uc', {
- token: this.code,
- username: this.form.username
- })
- .then(res => {
- if(res && res.data) {
- this.isProceeding = false;
- this.tabIndex = 2;
- } else {
- this.tabIndex = 'invalid-code';
- this.isProceeding = false;
- }
- })
- .catch(err => {
- if(err.response.data && err.response.data.username) {
- this.errors.username = err.response.data.username[0];
- this.isProceeding = false;
- } else {
- this.tabIndex = 'invalid-code';
- this.isProceeding = false;
- }
- })
- },
- checkEmailAvailability() {
- axios.post('/api/v1.1/auth/invite/admin/ec', {
- token: this.code,
- email: this.form.email
- })
- .then(res => {
- if(res && res.data) {
- this.isProceeding = false;
- this.tabIndex = 3;
- } else {
- this.tabIndex = 'invalid-code';
- this.isProceeding = false;
- }
- })
- .catch(err => {
- if(err.response.data && err.response.data.email) {
- this.errors.email = err.response.data.email[0];
- this.isProceeding = false;
- } else {
- this.tabIndex = 'invalid-code';
- this.isProceeding = false;
- }
- })
- },
- validateEmail() {
- if(!this.form.email || !this.form.email.length) {
- return false;
- }
- return /^[a-zA-Z]+[a-zA-Z0-9_.-]+@[a-zA-Z0-9_.-]+[a-zA-Z]$/i.test(this.form.email);
- },
- handleRegistration() {
- var $form = $('<form>', {
- action: '/api/v1.1/auth/invite/admin/re',
- method: 'post'
- });
- let fields = {
- '_token': document.head.querySelector('meta[name="csrf-token"]').content,
- token: this.code,
- username: this.form.username,
- name: this.form.display_name,
- email: this.form.email,
- password: this.form.password,
- password_confirm: this.form.password_confirm
- };
- $.each(fields, function(key, val) {
- $('<input>').attr({
- type: "hidden",
- name: key,
- value: val
- }).appendTo($form);
- });
- $form.appendTo('body').submit();
- },
- proceed(cur) {
- this.isProceeding = true;
- event.currentTarget.blur();
- switch(cur) {
- case 1:
- this.checkUsernameAvailability();
- break;
- case 2:
- this.checkEmailAvailability();
- break;
- case 3:
- this.isProceeding = false;
- this.tabIndex = 4;
- break;
- case 4:
- this.isProceeding = false;
- this.tabIndex = 5;
- break;
- case 5:
- this.tabIndex = 6;
- this.handleRegistration();
- break;
- }
- }
- }
- }
- </script>
- <style lang="scss">
- .admin-invite-component {
- font-family: var(--font-family-sans-serif);
- &-inner {
- display: flex;
- width: 100wv;
- height: 100vh;
- justify-content: center;
- align-items: center;
- .card {
- width: 100%;
- color: #fff;
- padding: 1.25rem 2.5rem;
- border-radius: 10px;
- min-height: 530px;
- @media(min-width: 768px) {
- width: 30%;
- }
- label {
- color: var(--muted);
- font-weight: bold;
- text-transform: uppercase;
- }
- .login-link {
- margin-top: 10px;
- font-weight: 600;
- }
- .register-terms {
- font-size: 12px;
- color: var(--muted);
- }
- .form-control {
- color: #fff;
- }
- .admin-message {
- margin-top: 20px;
- border: 1px solid var(--dropdown-item-hover-color);
- color: var(--text-lighter);
- padding: 1rem;
- border-radius: 5px;
- }
- }
- }
- }
- </style>
|