Refine the auth design; clean up the layout and styles there (#1240)

* Refine the auth design; clean up the layout and styles there

* It doesn't really sing, does it

* Tweak auth form spacing and wording

* Final tweaks to improved auth design

* Merge

* fix lockfile

---------

Co-authored-by: Prospector <prospectordev@gmail.com>
This commit is contained in:
falseresync
2023-08-18 22:00:44 +03:00
committed by GitHub
parent ce995812d4
commit 0ffe8ef102
21 changed files with 1849 additions and 398 deletions

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-key" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1 -4.069 0l-.301 -.301l-6.558 6.558a2 2 0 0 1 -1.239 .578l-.175 .008h-1.172a1 1 0 0 1 -.993 -.883l-.007 -.117v-1.172a2 2 0 0 1 .467 -1.284l.119 -.13l.414 -.414h2v-2h2v-2l2.144 -2.144l-.301 -.301a2.877 2.877 0 0 1 0 -4.069l2.643 -2.643a2.877 2.877 0 0 1 4.069 0z"></path>
<path d="M15 9h.01"></path>
</svg>

After

Width:  |  Height:  |  Size: 683 B

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-mail" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M3 7a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-10z"></path>
<path d="M3 7l9 6l9 -6"></path>
</svg>

After

Width:  |  Height:  |  Size: 425 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"><defs><style>.cls-1{fill:#5865f2;}</style></defs><g id="图层_2" data-name="图层 2"><g id="Discord_Logos" data-name="Discord Logos"><g id="Discord_Logo_-_Large_-_White" data-name="Discord Logo - Large - White"><path class="cls-1" d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/></g></g></g></svg>

After

Width:  |  Height:  |  Size: 989 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 97.63 96.03" fill="currentColor"> <path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"></path> </svg>

After

Width:  |  Height:  |  Size: 982 B

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="93.97 97.52 192.07 185">
<path fill="#e24329" d="m282.83 170.73-.27-.69-26.14-68.22a6.81 6.81 0 0 0-2.69-3.24 7 7 0 0 0-8 .43 7 7 0 0 0-2.32 3.52l-17.65 54h-71.47l-17.65-54a6.86 6.86 0 0 0-2.32-3.53 7 7 0 0 0-8-.43 6.87 6.87 0 0 0-2.69 3.24L97.44 170l-.26.69a48.54 48.54 0 0 0 16.1 56.1l.09.07.24.17 39.82 29.82 19.7 14.91 12 9.06a8.07 8.07 0 0 0 9.76 0l12-9.06 19.7-14.91 40.06-30 .1-.08a48.56 48.56 0 0 0 16.08-56.04Z" />
<path d="m282.83 170.73-.27-.69a88.3 88.3 0 0 0-35.15 15.8L190 229.25c19.55 14.79 36.57 27.64 36.57 27.64l40.06-30 .1-.08a48.56 48.56 0 0 0 16.1-56.08Z" fill="#fc6d26" />
<path fill="#fca326" d="m153.43 256.89 19.7 14.91 12 9.06a8.07 8.07 0 0 0 9.76 0l12-9.06 19.7-14.91S209.55 244 190 229.25c-19.55 14.75-36.57 27.64-36.57 27.64Z" />
<path d="M132.58 185.84A88.19 88.19 0 0 0 97.44 170l-.26.69a48.54 48.54 0 0 0 16.1 56.1l.09.07.24.17 39.82 29.82L190 229.21Z" fill="#fc6d26" />
</svg>

After

Width:  |  Height:  |  Size: 967 B

View File

@@ -0,0 +1,18 @@
<svg width="100%" height="100%" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<circle cx="50" cy="50" r="50" style="fill:white;"/>
<g id="Google__G__Logo.svg" transform="matrix(0.0991612,0,0,0.0991612,49.3739,50)">
<g transform="matrix(1,0,0,1,-352.8,-360)">
<clipPath id="_clip1">
<rect x="0" y="0" width="705.6" height="720"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(1,0,0,1,4477.16,2891.98)">
<path d="M-4117.16,-2597.44L-4117.16,-2458.02L-3923.42,-2458.02C-3931.93,-2413.18 -3957.46,-2375.22 -3995.75,-2349.69L-3878.91,-2259.03C-3810.84,-2321.87 -3771.56,-2414.16 -3771.56,-2523.8C-3771.56,-2549.33 -3773.85,-2573.87 -3778.11,-2597.43L-4117.16,-2597.44Z" style="fill:rgb(66,133,244);fill-rule:nonzero;"/>
<path d="M-4318.92,-2463.46L-4345.27,-2443.29L-4438.55,-2370.64C-4379.31,-2253.15 -4257.9,-2171.98 -4117.17,-2171.98C-4019.97,-2171.98 -3938.48,-2204.05 -3878.92,-2259.03L-3995.75,-2349.69C-4027.83,-2328.09 -4068.74,-2315 -4117.17,-2315C-4210.77,-2315 -4290.3,-2378.16 -4318.77,-2463.25L-4318.92,-2463.46Z" style="fill:rgb(52,168,83);fill-rule:nonzero;"/>
<path d="M-4438.55,-2693.33C-4463.09,-2644.89 -4477.16,-2590.24 -4477.16,-2531.99C-4477.16,-2473.73 -4463.09,-2419.08 -4438.55,-2370.64C-4438.55,-2370.32 -4318.76,-2463.59 -4318.76,-2463.59C-4325.96,-2485.19 -4330.22,-2508.09 -4330.22,-2531.99C-4330.22,-2555.88 -4325.96,-2578.79 -4318.76,-2600.39L-4438.55,-2693.33Z" style="fill:rgb(251,188,5);fill-rule:nonzero;"/>
<path d="M-4117.16,-2748.64C-4064.14,-2748.64 -4017.02,-2730.31 -3979.38,-2694.97L-3876.29,-2798.06C-3938.8,-2856.31 -4019.96,-2891.99 -4117.16,-2891.99C-4257.89,-2891.99 -4379.31,-2811.15 -4438.55,-2693.33L-4318.76,-2600.38C-4290.29,-2685.47 -4210.76,-2748.64 -4117.16,-2748.64Z" style="fill:rgb(234,67,53);fill-rule:nonzero;"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 321 B

View File

Before

Width:  |  Height:  |  Size: 838 B

After

Width:  |  Height:  |  Size: 839 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 127.14 96.36"><defs><style>.cls-1{fill:#fff;}</style></defs><g id="图层_2" data-name="图层 2"><g id="Discord_Logos" data-name="Discord Logos"><g id="Discord_Logo_-_Large_-_White" data-name="Discord Logo - Large - White"><path class="cls-1" d="M107.7,8.07A105.15,105.15,0,0,0,81.47,0a72.06,72.06,0,0,0-3.36,6.83A97.68,97.68,0,0,0,49,6.83,72.37,72.37,0,0,0,45.64,0,105.89,105.89,0,0,0,19.39,8.09C2.79,32.65-1.71,56.6.54,80.21h0A105.73,105.73,0,0,0,32.71,96.36,77.7,77.7,0,0,0,39.6,85.25a68.42,68.42,0,0,1-10.85-5.18c.91-.66,1.8-1.34,2.66-2a75.57,75.57,0,0,0,64.32,0c.87.71,1.76,1.39,2.66,2a68.68,68.68,0,0,1-10.87,5.19,77,77,0,0,0,6.89,11.1A105.25,105.25,0,0,0,126.6,80.22h0C129.24,52.84,122.09,29.11,107.7,8.07ZM42.45,65.69C36.18,65.69,31,60,31,53s5-12.74,11.43-12.74S54,46,53.89,53,48.84,65.69,42.45,65.69Zm42.24,0C78.41,65.69,73.25,60,73.25,53s5-12.74,11.44-12.74S96.23,46,96.12,53,91.08,65.69,84.69,65.69Z"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 985 B

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 380 380" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" fill="currentColor">
<g transform="matrix(1.97904,0,0,1.97904,-186.013,-186.006)">
<g id="LOGO">
<path d="M282.83,170.73L282.56,170.04L256.42,101.82C255.888,100.483 254.946,99.349 253.73,98.58C251.243,97.036 248.038,97.208 245.73,99.01C244.613,99.917 243.803,101.146 243.41,102.53L225.76,156.53L154.29,156.53L136.64,102.53C136.257,101.139 135.445,99.903 134.32,99C132.012,97.198 128.807,97.026 126.32,98.57C125.106,99.342 124.165,100.475 123.63,101.81L97.44,170L97.18,170.69C89.472,190.829 96.065,213.803 113.28,226.79L113.37,226.86L113.61,227.03L153.43,256.85L173.13,271.76L185.13,280.82C188.006,283.004 192.014,283.004 194.89,280.82L206.89,271.76L226.59,256.85L266.65,226.85L266.75,226.77C283.925,213.782 290.505,190.849 282.83,170.73Z" style="fill-rule:nonzero;"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<circle cx="50" cy="50" r="50" style="fill:white;"/>
<g id="Google__G__Logo.svg" transform="matrix(0.0991612,0,0,0.0991612,49.3739,50)">
<g transform="matrix(1,0,0,1,-352.8,-360)">
<clipPath id="_clip1">
<rect x="0" y="0" width="705.6" height="720"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(1,0,0,1,4477.16,2891.98)">
<path d="M-4117.16,-2597.44L-4117.16,-2458.02L-3923.42,-2458.02C-3931.93,-2413.18 -3957.46,-2375.22 -3995.75,-2349.69L-3878.91,-2259.03C-3810.84,-2321.87 -3771.56,-2414.16 -3771.56,-2523.8C-3771.56,-2549.33 -3773.85,-2573.87 -3778.11,-2597.43L-4117.16,-2597.44Z" style="fill:rgb(66,133,244);fill-rule:nonzero;"/>
<path d="M-4318.92,-2463.46L-4345.27,-2443.29L-4438.55,-2370.64C-4379.31,-2253.15 -4257.9,-2171.98 -4117.17,-2171.98C-4019.97,-2171.98 -3938.48,-2204.05 -3878.92,-2259.03L-3995.75,-2349.69C-4027.83,-2328.09 -4068.74,-2315 -4117.17,-2315C-4210.77,-2315 -4290.3,-2378.16 -4318.77,-2463.25L-4318.92,-2463.46Z" style="fill:rgb(52,168,83);fill-rule:nonzero;"/>
<path d="M-4438.55,-2693.33C-4463.09,-2644.89 -4477.16,-2590.24 -4477.16,-2531.99C-4477.16,-2473.73 -4463.09,-2419.08 -4438.55,-2370.64C-4438.55,-2370.32 -4318.76,-2463.59 -4318.76,-2463.59C-4325.96,-2485.19 -4330.22,-2508.09 -4330.22,-2531.99C-4330.22,-2555.88 -4325.96,-2578.79 -4318.76,-2600.39L-4438.55,-2693.33Z" style="fill:rgb(251,188,5);fill-rule:nonzero;"/>
<path d="M-4117.16,-2748.64C-4064.14,-2748.64 -4017.02,-2730.31 -3979.38,-2694.97L-3876.29,-2798.06C-3938.8,-2856.31 -4019.96,-2891.99 -4117.16,-2891.99C-4257.89,-2891.99 -4379.31,-2811.15 -4438.55,-2693.33L-4318.76,-2600.38C-4290.29,-2685.47 -4210.76,-2748.64 -4117.16,-2748.64Z" style="fill:rgb(234,67,53);fill-rule:nonzero;"/>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -304,6 +304,9 @@ export default defineNuxtConfig({
nitro: { nitro: {
moduleSideEffects: ['@vintl/compact-number/locale-data'], moduleSideEffects: ['@vintl/compact-number/locale-data'],
}, },
devtools: {
enabled: true,
},
}) })
function getApiUrl() { function getApiUrl() {

View File

@@ -13,6 +13,7 @@
}, },
"devDependencies": { "devDependencies": {
"@formatjs/cli": "^6.1.2", "@formatjs/cli": "^6.1.2",
"@nuxt/devtools": "^0.7.0",
"@nuxtjs/eslint-config-typescript": "^12.0.0", "@nuxtjs/eslint-config-typescript": "^12.0.0",
"@nuxtjs/turnstile": "^0.5.0", "@nuxtjs/turnstile": "^0.5.0",
"@types/node": "^20.1.0", "@types/node": "^20.1.0",

View File

@@ -1,159 +1,83 @@
<template> <template>
<div> <NuxtPage class="auth-container universal-card" />
<NuxtPage class="auth-container universal-card" :route="route" />
</div>
</template> </template>
<script setup> <style>
const route = useRoute()
</script>
<style lang="scss">
.auth-container { .auth-container {
width: 25rem; width: 30rem;
padding: var(--gap-xl);
background-color: var(--color-raised-bg);
border-radius: var(--radius-lg);
margin: 2rem auto; margin: 2rem auto;
display: flex;
flex-direction: column;
gap: 2rem;
}
h1 { .auth-container h1 {
margin: 0; font-size: var(--font-size-xl);
margin: 0 0 -1rem 0;
color: var(--color-contrast); color: var(--color-contrast);
} }
h2 { .auth-container p {
font-size: 1.25rem;
font-weight: 500;
margin: 0;
color: var(--color-contrast);
}
p {
margin: 0; margin: 0;
} }
.btn { .auth-container .btn {
font-weight: 700; font-weight: 700;
min-height: 2.5rem; min-height: 2.5rem;
text-decoration: none; text-decoration: none;
} }
input { .centered-btn {
width: 100%; margin-inline: auto;
border: none;
outline: none;
} }
.btn.right-icon svg { .btn.continue-btn svg {
margin-left: var(--gap-sm);
}
.btn.left-icon svg {
margin-right: var(--gap-sm);
}
.input-group {
display: flex;
gap: var(--gap-md);
flex-wrap: wrap;
}
button.checkbox {
appearance: none !important;
border: none;
}
.continue-btn {
margin-left: auto;
margin-right: auto;
margin-block-start: 0;
}
.continue-btn svg {
margin: 0 0 0 0.5rem; margin: 0 0 0 0.5rem;
} }
// login styles
.third-party { .third-party {
display: grid; display: grid;
gap: var(--gap-md); gap: var(--gap-md);
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
width: 100%; width: 100%;
} }
.third-party .btn { .third-party .btn {
width: 100%; width: 100%;
justify-content: center;
vertical-align: middle; vertical-align: middle;
} }
.third-party .btn svg { .third-party .btn svg {
margin-right: var(--gap-sm); margin-right: var(--gap-sm);
width: 1.25rem; width: 1.25rem;
height: 1.25rem; height: 1.25rem;
} }
.discord-btn {
color: #ffffff;
background-color: #5865f2;
}
.apple-btn {
color: var(--color-accent-contrast);
background-color: var(--color-contrast);
}
.google-btn {
color: #ffffff;
background-color: #4285f4;
}
.gitlab-btn {
color: #ffffff;
background-color: #fc6d26;
}
.github-btn {
color: #ffffff;
background-color: #8740f1;
}
.microsoft-btn {
color: var(--color-accent-contrast);
background-color: var(--color-contrast);
}
.text-divider {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
}
.text-divider div {
height: 2px;
width: 100%;
max-width: 5rem;
opacity: 40%;
border-radius: var(--radius-max);
background-color: var(--color-base);
}
.text-divider span {
margin-inline: var(--gap-sm);
}
@media screen and (max-width: 25.5rem) { @media screen and (max-width: 25.5rem) {
width: auto;
margin: 1rem;
.third-party .btn { .third-party .btn {
grid-column: 1 / 3; grid-column: 1 / 3;
} }
} }
.turnstile {
display: none;
} }
.auth-page-container { .auth-form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--gap-md); gap: var(--gap-md);
} }
.auth-form .auth-form__input {
width: 100%;
flex-basis: auto;
}
.auth-form__additional-options {
align-items: center;
display: flex;
justify-content: center;
gap: var(--gap-md);
}
</style> </style>

View File

@@ -1,31 +1,69 @@
<template> <template>
<div class="auth-page-container"> <div>
<h1>Reset your password</h1> <h1>Reset your password</h1>
<section class="auth-form">
<template v-if="step === 'choose_method'"> <template v-if="step === 'choose_method'">
<p> <p>
Enter your email below and we'll send a recovery link to allow you to recover your account. Enter your email below and we'll send a recovery link to allow you to recover your
account.
<NuxtTurnstile ref="turnstile" v-model="token" class="turnstile" /> <NuxtTurnstile ref="turnstile" v-model="token" class="turnstile" />
</p> </p>
<div class="iconified-input">
<label for="email" hidden>Email or username</label> <label for="email" hidden>Email or username</label>
<input id="email" v-model="email" type="text" placeholder="Email or username" /> <MailIcon />
<button class="btn btn-primary continue-btn" @click="recovery">Send recovery email</button> <input
id="email"
v-model="email"
type="text"
class="auth-form__input"
placeholder="Email"
/>
</div>
<button class="btn btn-primary centered-btn" @click="recovery">
<SendIcon /> Send recovery email
</button>
</template> </template>
<template v-else-if="step === 'passed_challenge'"> <template v-else-if="step === 'passed_challenge'">
<p>Enter your new password below to gain access to your account.</p> <p>Enter your new password below to gain access to your account.</p>
<div class="iconified-input">
<label for="password" hidden>Password</label> <label for="password" hidden>Password</label>
<input id="password" v-model="newPassword" type="password" placeholder="Password" /> <KeyIcon />
<label for="confirm-password" hi2dden>Password</label> <input
id="password"
v-model="newPassword"
type="password"
class="auth-form__input"
placeholder="Password"
/>
</div>
<div class="iconified-input">
<label for="confirm-password" hidden>Password</label>
<KeyIcon />
<input <input
id="confirm-password" id="confirm-password"
v-model="confirmNewPassword" v-model="confirmNewPassword"
type="password" type="password"
class="auth-form__input"
placeholder="Confirm password" placeholder="Confirm password"
/> />
<button class="btn btn-primary continue-btn" @click="changePassword">Reset password</button> </div>
<button class="auth-form__input btn btn-primary continue-btn" @click="changePassword">
Reset password
</button>
</template> </template>
</section>
</div> </div>
</template> </template>
<script setup> <script setup>
import { SendIcon } from 'omorphia'
import MailIcon from 'assets/icons/auth/mail.svg'
import KeyIcon from 'assets/icons/auth/key.svg'
useHead({ useHead({
title: 'Reset Password - Modrinth', title: 'Reset Password - Modrinth',
}) })
@@ -35,7 +73,6 @@ if (auth.value.user) {
await navigateTo('/dashboard') await navigateTo('/dashboard')
} }
const data = useNuxtApp()
const route = useRoute() const route = useRoute()
const step = ref('choose_method') const step = ref('choose_method')
@@ -60,14 +97,14 @@ async function recovery() {
}, },
}) })
data.$notify({ addNotification({
group: 'main', group: 'main',
title: 'Email sent', title: 'Email sent',
text: 'An email with instructions has been sent to you if the email was previously saved on your account.', text: 'An email with instructions has been sent to you if the email was previously saved on your account.',
type: 'success', type: 'success',
}) })
} catch (err) { } catch (err) {
data.$notify({ addNotification({
group: 'main', group: 'main',
title: 'An error occurred', title: 'An error occurred',
text: err.data ? err.data.description : err, text: err.data ? err.data.description : err,
@@ -92,7 +129,7 @@ async function changePassword() {
}, },
}) })
data.$notify({ addNotification({
group: 'main', group: 'main',
title: 'Password successfully reset', title: 'Password successfully reset',
text: 'You can now log-in into your account with your new password.', text: 'You can now log-in into your account with your new password.',
@@ -100,7 +137,7 @@ async function changePassword() {
}) })
await navigateTo('/auth/sign-in') await navigateTo('/auth/sign-in')
} catch (err) { } catch (err) {
data.$notify({ addNotification({
group: 'main', group: 'main',
title: 'An error occurred', title: 'An error occurred',
text: err.data ? err.data.description : err, text: err.data ? err.data.description : err,

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="auth-page-container"> <div>
<template v-if="flow"> <template v-if="flow">
<label for="two-factor-code"> <label for="two-factor-code">
<span class="label__title">Enter two-factor code</span> <span class="label__title">Enter two-factor code</span>
@@ -13,74 +13,101 @@
placeholder="Enter code..." placeholder="Enter code..."
/> />
<button class="btn btn-primary continue-btn" @click="loginTwoFactor"> <button class="btn btn-primary continue-btn" @click="begin2FASignIn">
Sign in <RightArrowIcon /> Sign in <RightArrowIcon />
</button> </button>
</template> </template>
<template v-else> <template v-else>
<h1>Continue with</h1> <h1>Sign in with</h1>
<div class="third-party">
<a class="btn discord-btn" :href="getAuthUrl('discord')"> <section class="third-party">
<DiscordIcon /> <span>Discord</span> <a class="btn" :href="getAuthUrl('discord')">
<DiscordIcon />
<span>Discord</span>
</a> </a>
<a class="btn github-btn" :href="getAuthUrl('github')" <a class="btn" :href="getAuthUrl('github')">
><GitHubIcon /> <span>GitHub</span></a <GitHubIcon />
> <span>GitHub</span>
<a class="btn microsoft-btn" :href="getAuthUrl('microsoft')">
<MicrosoftIcon /> <span>Microsoft</span>
</a> </a>
<a class="btn google-btn" :href="getAuthUrl('google')"> <a class="btn" :href="getAuthUrl('microsoft')">
<GoogleIcon /> <span>Google</span> <MicrosoftIcon />
<span>Microsoft</span>
</a> </a>
<a class="btn apple-btn" :href="getAuthUrl('steam')"><SteamIcon /> <span>Steam</span></a> <a class="btn" :href="getAuthUrl('google')">
<a class="btn gitlab-btn" :href="getAuthUrl('gitlab')"> <GoogleIcon />
<GitLabIcon /> <span>GitLab</span></a <span>Google</span>
> </a>
</div> <a class="btn" :href="getAuthUrl('steam')">
<div class="text-divider"> <SteamIcon />
<div></div> <span>Steam</span>
<span>or</span> </a>
<div></div> <a class="btn" :href="getAuthUrl('gitlab')">
</div> <GitLabIcon />
<span>GitLab</span>
</a>
</section>
<h1>Or use a password</h1>
<section class="auth-form">
<div class="iconified-input">
<label for="email" hidden>Email or username</label> <label for="email" hidden>Email or username</label>
<input id="email" v-model="email" type="text" placeholder="Email or username" /> <MailIcon />
<label for="password" hidden>Password</label> <input
<input id="password" v-model="password" type="password" placeholder="Password" /> id="email"
<div class="account-options"> v-model="email"
<NuxtTurnstile ref="turnstile" v-model="token" class="turnstile" /> type="text"
<nuxt-link class="text-link" to="/auth/reset-password">Forgot password?</nuxt-link> class="auth-form__input"
placeholder="Email or username"
/>
</div> </div>
<button class="btn btn-primary continue-btn" @click="loginPassword()">
Continue <RightArrowIcon /> <div class="iconified-input">
<label for="password" hidden>Password</label>
<KeyIcon />
<input
id="password"
v-model="password"
type="password"
class="auth-form__input"
placeholder="Password"
/>
</div>
<NuxtTurnstile ref="turnstile" v-model="token" class="turnstile" />
<button class="btn btn-primary continue-btn centered-btn" @click="beginPasswordSignIn()">
Sign in <RightArrowIcon />
</button> </button>
<p>
Don't have an account yet? <div class="auth-form__additional-options">
<nuxt-link <NuxtLink class="text-link" to="/auth/reset-password">Forgot password?</NuxtLink>
class="text-link" <p></p>
:to="`/auth/sign-up${route.query.redirect ? `?redirect=${route.query.redirect}` : ''}`" <NuxtLink class="text-link" :to="signUpLink"> Create an account</NuxtLink>
> </div>
Create one. </section>
</nuxt-link>
</p>
</template> </template>
</div> </div>
</template> </template>
<script setup> <script setup>
import { GitHubIcon, RightArrowIcon } from 'omorphia' import { RightArrowIcon } from 'omorphia'
import DiscordIcon from 'assets/images/utils/discord.svg' import GitHubIcon from 'assets/icons/auth/sso-github.svg'
import GoogleIcon from 'assets/images/utils/google.svg' import MicrosoftIcon from 'assets/icons/auth/sso-microsoft.svg'
import SteamIcon from 'assets/images/utils/steam.svg' import GoogleIcon from 'assets/icons/auth/sso-google.svg'
import MicrosoftIcon from 'assets/images/utils/microsoft.svg' import SteamIcon from 'assets/icons/auth/sso-steam.svg'
import GitLabIcon from 'assets/images/utils/gitlab.svg' import DiscordIcon from 'assets/icons/auth/sso-discord.svg'
import KeyIcon from 'assets/icons/auth/key.svg'
import MailIcon from 'assets/icons/auth/mail.svg'
import GitLabIcon from 'assets/icons/auth/sso-gitlab.svg'
useHead({ useHead({
title: 'Sign In - Modrinth', title: 'Sign In - Modrinth',
}) })
const auth = await useAuth() const auth = await useAuth()
const route = useRoute() const route = useRoute()
if (route.fullPath.includes('new_account=true')) { if (route.fullPath.includes('new_account=true')) {
await navigateTo( await navigateTo(
`/auth/welcome?authToken=${route.query.code}${ `/auth/welcome?authToken=${route.query.code}${
@@ -88,15 +115,13 @@ if (route.fullPath.includes('new_account=true')) {
}` }`
) )
} else if (route.query.code) { } else if (route.query.code) {
await loginHandler() await finishSignIn()
} }
if (auth.value.user) { if (auth.value.user) {
await navigateTo('/dashboard') await navigateTo('/dashboard')
} }
const data = useNuxtApp()
const turnstile = ref() const turnstile = ref()
const email = ref('') const email = ref('')
@@ -105,7 +130,11 @@ const token = ref('')
const flow = ref(route.query.flow) const flow = ref(route.query.flow)
async function loginPassword() { const signUpLink = computed(
() => `/auth/sign-up${route.query.redirect ? `?redirect=${route.query.redirect}` : ''}`
)
async function beginPasswordSignIn() {
startLoading() startLoading()
try { try {
const res = await useBaseFetch('auth/login', { const res = await useBaseFetch('auth/login', {
@@ -120,10 +149,10 @@ async function loginPassword() {
if (res.flow) { if (res.flow) {
flow.value = res.flow flow.value = res.flow
} else { } else {
await loginHandler(res.session) await finishSignIn(res.session)
} }
} catch (err) { } catch (err) {
data.$notify({ addNotification({
group: 'main', group: 'main',
title: 'An error occurred', title: 'An error occurred',
text: err.data ? err.data.description : err, text: err.data ? err.data.description : err,
@@ -135,7 +164,7 @@ async function loginPassword() {
} }
const twoFactorCode = ref(null) const twoFactorCode = ref(null)
async function loginTwoFactor() { async function begin2FASignIn() {
startLoading() startLoading()
try { try {
const res = await useBaseFetch('auth/login/2fa', { const res = await useBaseFetch('auth/login/2fa', {
@@ -146,9 +175,9 @@ async function loginTwoFactor() {
}, },
}) })
await loginHandler(res.session) await finishSignIn(res.session)
} catch (err) { } catch (err) {
data.$notify({ addNotification({
group: 'main', group: 'main',
title: 'An error occurred', title: 'An error occurred',
text: err.data ? err.data.description : err, text: err.data ? err.data.description : err,
@@ -158,7 +187,7 @@ async function loginTwoFactor() {
} }
stopLoading() stopLoading()
} }
async function loginHandler(token) { async function finishSignIn(token) {
if (token) { if (token) {
await useAuth(token) await useAuth(token)
await useUser() await useUser()
@@ -171,25 +200,3 @@ async function loginHandler(token) {
} }
} }
</script> </script>
<style lang="scss" scoped>
.totp {
justify-content: center;
}
.totp-codes {
justify-content: center;
display: grid;
gap: var(--gap-md);
grid-template-columns: repeat(2, 1fr);
width: 100%;
}
.account-options {
display: flex;
width: 100%;
margin-block-start: 0 !important;
}
.account-options a {
margin-left: auto;
}
</style>

View File

@@ -1,71 +1,121 @@
<template> <template>
<div class="auth-page-container"> <div>
<h1>Create your account</h1> <h1>Sign up with</h1>
<div class="third-party">
<section class="third-party">
<a class="btn discord-btn" :href="getAuthUrl('discord')"> <a class="btn discord-btn" :href="getAuthUrl('discord')">
<DiscordIcon /> <span>Discord</span> <DiscordIcon />
<span>Discord</span>
</a> </a>
<a class="btn github-btn" :href="getAuthUrl('github')"><GitHubIcon /> <span>GitHub</span></a> <a class="btn" :href="getAuthUrl('github')">
<a class="btn microsoft-btn" :href="getAuthUrl('microsoft')"> <GitHubIcon />
<MicrosoftIcon /> <span>Microsoft</span> <span>GitHub</span>
</a> </a>
<a class="btn google-btn" :href="getAuthUrl('google')"> <a class="btn" :href="getAuthUrl('microsoft')">
<GoogleIcon /> <span>Google</span> <MicrosoftIcon />
<span>Microsoft</span>
</a> </a>
<a class="btn apple-btn" :href="getAuthUrl('steam')"><SteamIcon /> <span>Steam</span></a> <a class="btn" :href="getAuthUrl('google')">
<a class="btn gitlab-btn" :href="getAuthUrl('gitlab')"> <GitLabIcon /> <span>GitLab</span></a> <GoogleIcon />
</div> <span>Google</span>
<div class="text-divider"> </a>
<div></div> <a class="btn" :href="getAuthUrl('steam')">
<span>or</span> <SteamIcon />
<div></div> <span>Steam</span>
</div> </a>
<a class="btn" :href="getAuthUrl('gitlab')">
<GitLabIcon />
<span>GitLab</span>
</a>
</section>
<h1>Or create an account yourself</h1>
<section class="auth-form">
<div class="iconified-input">
<label for="email" hidden>Email</label> <label for="email" hidden>Email</label>
<input id="email" v-model="email" type="text" placeholder="Email" /> <MailIcon />
<input
id="email"
v-model="email"
type="text"
class="auth-form__input"
placeholder="Email"
/>
</div>
<div class="iconified-input">
<label for="username" hidden>Username</label> <label for="username" hidden>Username</label>
<input id="username" v-model="username" type="text" placeholder="Username" /> <UserIcon />
<input
id="username"
v-model="username"
type="text"
class="auth-form__input"
placeholder="Username"
/>
</div>
<div class="iconified-input">
<label for="password" hidden>Password</label> <label for="password" hidden>Password</label>
<input id="password" v-model="password" type="password" placeholder="Password" /> <KeyIcon />
<input
id="password"
v-model="password"
class="auth-form__input"
type="password"
placeholder="Password"
/>
</div>
<div class="iconified-input">
<label for="confirm-password" hidden>Password</label> <label for="confirm-password" hidden>Password</label>
<KeyIcon />
<input <input
id="confirm-password" id="confirm-password"
v-model="confirmPassword" v-model="confirmPassword"
type="password" type="password"
class="auth-form__input"
placeholder="Confirm password" placeholder="Confirm password"
/> />
</div>
<NuxtTurnstile ref="turnstile" v-model="token" class="turnstile" />
<Checkbox <Checkbox
v-model="subscribe" v-model="subscribe"
class="subscribe-btn" class="subscribe-btn"
label="Subscribe to updates about Modrinth" label="Subscribe to updates about Modrinth"
/> />
<p> <p>
By creating an account, you agree to Modrinth's By creating an account, you agree to Modrinth's
<nuxt-link to="/legal/terms" class="text-link">terms</nuxt-link> and <NuxtLink to="/legal/terms" class="text-link">Terms</NuxtLink> and
<nuxt-link to="/legal/privacy" class="text-link">privacy policy</nuxt-link>. <NuxtLink to="/legal/privacy" class="text-link">Privacy Policy</NuxtLink>.
</p> </p>
<button class="btn btn-primary continue-btn" @click="createAccount">
<button class="btn btn-primary continue-btn centered-btn" @click="createAccount">
Create account <RightArrowIcon /> Create account <RightArrowIcon />
</button> </button>
<p>
<div class="auth-form__additional-options">
Already have an account? Already have an account?
<nuxt-link <NuxtLink class="text-link" :to="signInLink">Sign in</NuxtLink>
class="text-link" </div>
:to="`/auth/sign-in${route.query.redirect ? `?redirect=${route.query.redirect}` : ''}`" </section>
>
Sign in.
</nuxt-link>
<NuxtTurnstile ref="turnstile" v-model="token" class="turnstile" />
</p>
</div> </div>
</template> </template>
<script setup> <script setup>
import { GitHubIcon, RightArrowIcon, Checkbox } from 'omorphia' import { RightArrowIcon, UserIcon, Checkbox } from 'omorphia'
import DiscordIcon from 'assets/images/utils/discord.svg' import GitHubIcon from 'assets/icons/auth/sso-github.svg'
import GoogleIcon from 'assets/images/utils/google.svg' import MicrosoftIcon from 'assets/icons/auth/sso-microsoft.svg'
import SteamIcon from 'assets/images/utils/steam.svg' import GoogleIcon from 'assets/icons/auth/sso-google.svg'
import MicrosoftIcon from 'assets/images/utils/microsoft.svg' import SteamIcon from 'assets/icons/auth/sso-steam.svg'
import GitLabIcon from 'assets/images/utils/gitlab.svg' import DiscordIcon from 'assets/icons/auth/sso-discord.svg'
import KeyIcon from 'assets/icons/auth/key.svg'
import MailIcon from 'assets/icons/auth/mail.svg'
import GitLabIcon from 'assets/icons/auth/sso-gitlab.svg'
useHead({ useHead({
title: 'Sign Up - Modrinth', title: 'Sign Up - Modrinth',
@@ -86,8 +136,6 @@ if (auth.value.user) {
await navigateTo('/dashboard') await navigateTo('/dashboard')
} }
const data = useNuxtApp()
const turnstile = ref() const turnstile = ref()
const email = ref('') const email = ref('')
@@ -97,11 +145,15 @@ const confirmPassword = ref('')
const token = ref('') const token = ref('')
const subscribe = ref(true) const subscribe = ref(true)
const signInLink = computed(
() => `/auth/sign-in${route.query.redirect ? `?redirect=${route.query.redirect}` : ''}`
)
async function createAccount() { async function createAccount() {
startLoading() startLoading()
try { try {
if (confirmPassword.value !== password.value) { if (confirmPassword.value !== password.value) {
data.$notify({ addNotification({
group: 'main', group: 'main',
title: 'An error occurred', title: 'An error occurred',
text: 'Passwords do not match!', text: 'Passwords do not match!',
@@ -130,7 +182,7 @@ async function createAccount() {
await navigateTo('/dashboard') await navigateTo('/dashboard')
} }
} catch (err) { } catch (err) {
data.$notify({ addNotification({
group: 'main', group: 'main',
title: 'An error occurred', title: 'An error occurred',
text: err.data ? err.data.description : err, text: err.data ? err.data.description : err,
@@ -141,8 +193,3 @@ async function createAccount() {
stopLoading() stopLoading()
} }
</script> </script>
<style lang="scss" scoped>
.subscribe-btn {
margin-block-start: 0 !important;
}
</style>

View File

@@ -1,24 +1,34 @@
<template> <template>
<div class="auth-page-container"> <div>
<template v-if="auth.user && auth.user.email_verified && !success"> <template v-if="auth.user && auth.user.email_verified && !success">
<h1>Email already verified</h1> <h1>Email already verified</h1>
<section class="auth-form">
<p>Your email is already verified!</p> <p>Your email is already verified!</p>
<nuxt-link class="btn" link="/settings/account">
<SettingsIcon /> Account settings <NuxtLink class="btn" to="/settings/account"> <SettingsIcon /> Account settings </NuxtLink>
</nuxt-link> </section>
</template> </template>
<template v-else-if="success"> <template v-else-if="success">
<h1>Email verification</h1> <h1>Email verification</h1>
<section class="auth-form">
<p>Your email address has been successfully verified!</p> <p>Your email address has been successfully verified!</p>
<nuxt-link v-if="auth.user" class="btn" to="/settings/account">
<NuxtLink v-if="auth.user" class="btn" link="/settings/account">
<SettingsIcon /> Account settings <SettingsIcon /> Account settings
</nuxt-link> </NuxtLink>
<nuxt-link v-else to="/auth/sign-in" class="btn btn-primary continue-btn"> <NuxtLink v-else to="/auth/sign-in" class="btn btn-primary continue-btn centered-btn">
Sign in <RightArrowIcon /> Sign in <RightArrowIcon />
</nuxt-link> </NuxtLink>
</section>
</template> </template>
<template v-else> <template v-else>
<h1>Email verification failed</h1> <h1>Email verification failed</h1>
<section class="auth-form">
<p> <p>
We were unable to verify your email. We were unable to verify your email.
<template v-if="auth.user"> <template v-if="auth.user">
@@ -28,12 +38,15 @@
Try re-sending the verification email through your dashboard by signing in. Try re-sending the verification email through your dashboard by signing in.
</template> </template>
</p> </p>
<button v-if="auth.user" class="btn btn-primary continue-btn" @click="resendVerifyEmail"> <button v-if="auth.user" class="btn btn-primary continue-btn" @click="resendVerifyEmail">
Resend verification email <RightArrowIcon /> Resend verification email <RightArrowIcon />
</button> </button>
<nuxt-link v-else to="/auth/sign-in" class="btn btn-primary continue-btn">
<NuxtLink v-else to="/auth/sign-in" class="btn btn-primary continue-btn centered-btn">
Sign in <RightArrowIcon /> Sign in <RightArrowIcon />
</nuxt-link> </NuxtLink>
</section>
</template> </template>
</div> </div>
</template> </template>

View File

@@ -1,25 +1,33 @@
<template> <template>
<div class="auth-page-container"> <div>
<h1>Welcome to Modrinth!</h1> <h1>Welcome to Modrinth!</h1>
<section class="auth-form">
<p> <p>
Thank you for creating an account. You can now follow and create projects, receive updates Thank you for creating an account. You can now follow and create projects, receive updates
about your favorite projects, and more! about your favorite projects, and more!
</p> </p>
<Checkbox <Checkbox
v-model="subscribe" v-model="subscribe"
class="subscribe-btn" class="subscribe-btn"
label="Subscribe to updates about Modrinth" label="Subscribe to updates about Modrinth"
/> />
<button class="btn btn-primary continue-btn" @click="continueSignUp">Continue</button>
<button class="btn btn-primary continue-btn centered-btn" @click="continueSignUp">
Continue <RightArrowIcon />
</button>
<p> <p>
By creating an account, you agree to Modrinth's By creating an account, you have agreed to Modrinth's
<nuxt-link to="/legal/terms" class="text-link">terms</nuxt-link> and <NuxtLink to="/legal/terms" class="text-link">Terms</NuxtLink> and
<nuxt-link to="/legal/privacy" class="text-link">privacy policy</nuxt-link>. <NuxtLink to="/legal/privacy" class="text-link">Privacy Policy</NuxtLink>.
</p> </p>
</section>
</div> </div>
</template> </template>
<script setup> <script setup>
import { Checkbox } from 'omorphia' import { Checkbox, RightArrowIcon } from 'omorphia'
useHead({ useHead({
title: 'Welcome - Modrinth', title: 'Welcome - Modrinth',

View File

@@ -381,16 +381,16 @@ import {
LeftArrowIcon, LeftArrowIcon,
RightArrowIcon, RightArrowIcon,
CheckIcon, CheckIcon,
GitHubIcon,
ExternalIcon, ExternalIcon,
} from 'omorphia' } from 'omorphia'
import QrcodeVue from 'qrcode.vue' import QrcodeVue from 'qrcode.vue'
import DiscordIcon from 'assets/images/utils/discord.svg' import GitHubIcon from 'assets/icons/auth/sso-github.svg'
import GoogleIcon from 'assets/images/utils/google.svg' import MicrosoftIcon from 'assets/icons/auth/sso-microsoft.svg'
import SteamIcon from 'assets/images/utils/steam.svg' import GoogleIcon from 'assets/icons/auth/sso-google.svg'
import MicrosoftIcon from 'assets/images/utils/microsoft.svg' import SteamIcon from 'assets/icons/auth/sso-steam.svg'
import GitLabIcon from 'assets/images/utils/gitlab.svg' import DiscordIcon from 'assets/icons/auth/sso-discord.svg'
import KeyIcon from '~/assets/images/utils/key.svg' import KeyIcon from 'assets/icons/auth/key.svg'
import GitLabIcon from 'assets/icons/auth/sso-gitlab.svg'
import ModalConfirm from '~/components/ui/ModalConfirm.vue' import ModalConfirm from '~/components/ui/ModalConfirm.vue'
import Modal from '~/components/ui/Modal.vue' import Modal from '~/components/ui/Modal.vue'

1411
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff