feat: 211025 base + onboard FE
This commit is contained in:
parent
bc6002f70c
commit
2414cad2d2
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { LayoutVue } from '@/components/common'
|
||||
import { LayoutVue } from '@/components'
|
||||
|
||||
const config = {
|
||||
token: {
|
||||
|
||||
@ -288,3 +288,133 @@ body {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn-common {
|
||||
@include baseBtn(
|
||||
$bg: var(--vt-c-main-color),
|
||||
$width: unset,
|
||||
$height: unset,
|
||||
$borderRadius: unset
|
||||
);
|
||||
margin: 0 auto 5px;
|
||||
padding: var(--vt-btn-padding);
|
||||
color: var(--vt-c-white);
|
||||
font-weight: 500;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
border: 2px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
&:hover {
|
||||
border: 2px solid var(--vt-c-black-bold);
|
||||
color: var(--vt-c-black-bold);
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1rem;
|
||||
margin: 1em;
|
||||
line-height: 1.5;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: var(--vt-c-main-color);
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: var(--vt-c-main-color);
|
||||
}
|
||||
|
||||
.spacer {
|
||||
height: 1px;
|
||||
width: 90%;
|
||||
background: linear-gradient(to right, transparent, rgb(224, 224, 224), transparent);
|
||||
margin: 2rem auto;
|
||||
}
|
||||
|
||||
.note {
|
||||
margin-top: 1rem;
|
||||
font-size: 0.9rem;
|
||||
color: --vt-c-gray-note;
|
||||
}
|
||||
|
||||
.auth-container {
|
||||
@include center_flex;
|
||||
min-height: 100vh;
|
||||
background-color: var(--vt-c-white);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.auth-card {
|
||||
background-color: var(--vt-c-white);
|
||||
border: 2px solid var(--vt-c-main-color);
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
padding: 0;
|
||||
|
||||
&-header {
|
||||
padding: 5px 10px;
|
||||
min-height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid var(--vt-c-main-color);
|
||||
|
||||
h2 {
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
color: var(--vt-c-black-bold);
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.wallet-icon {
|
||||
margin: 20px 0 30px 0;
|
||||
|
||||
.icon-circle {
|
||||
@include center_flex;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background-color: var(--vt-c-main-color);
|
||||
border-radius: 50%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.password-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.auth-button-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
|
||||
.auth-btn {
|
||||
flex: 1;
|
||||
border-radius: 4px;
|
||||
padding: 12px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&.secondary {
|
||||
background-color: var(--vt-c-white);
|
||||
color: var(--vt-c-black-bold);
|
||||
border: 1px solid var(--vt-c-black-bold);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--vt-c-white-soft);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
:root {
|
||||
--vt-btn-padding: 11px 8px;
|
||||
--vt-c-main-color: #009688;
|
||||
--vt-c-badge-caption-shadow: rgba(0, 151, 115, 1);
|
||||
--vt-input-shadow: 0 0 0 2px rgba(0, 150, 136, 0.1);
|
||||
--vt-input-shadow-focus: 0 0 0 2px rgba(0, 150, 136, 0.1);
|
||||
--vt-c-gray-note: rgb(85, 85, 85);
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
61
src/components/auth/CreateWalletComponent.vue
Normal file
61
src/components/auth/CreateWalletComponent.vue
Normal file
@ -0,0 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ButtonCommon, FormCommon } from '@/components'
|
||||
|
||||
const password = ref('')
|
||||
const confirmPassword = ref('')
|
||||
|
||||
const handleCancel = () => {}
|
||||
|
||||
const handleIHaveWallet = () => {}
|
||||
|
||||
const handleNext = () => {}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="auth-container">
|
||||
<div class="auth-card">
|
||||
<div class="auth-card-header">
|
||||
<h2>Create Wallet</h2>
|
||||
</div>
|
||||
|
||||
<div class="auth-card-content">
|
||||
<div class="password-section">
|
||||
<FormCommon
|
||||
v-model="password"
|
||||
type="password"
|
||||
label="Create a password for your new wallet"
|
||||
placeholder="Password"
|
||||
show-password-toggle
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="password-section">
|
||||
<FormCommon
|
||||
v-model="confirmPassword"
|
||||
type="password"
|
||||
label="Confirm password"
|
||||
placeholder="Confirm Password"
|
||||
show-password-toggle
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="auth-button-group">
|
||||
<ButtonCommon class="auth-btn secondary" @click="handleCancel">
|
||||
Cancel
|
||||
</ButtonCommon>
|
||||
|
||||
<ButtonCommon class="auth-btn secondary" @click="handleIHaveWallet">
|
||||
I have a wallet
|
||||
</ButtonCommon>
|
||||
|
||||
<ButtonCommon class="auth-btn primary" @click="handleNext"> Next </ButtonCommon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
62
src/components/auth/OnboardingComponent.vue
Normal file
62
src/components/auth/OnboardingComponent.vue
Normal file
@ -0,0 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
import { ButtonCommon } from '@/components'
|
||||
|
||||
const goToNewWallet = () => {
|
||||
window.open('https://kaspa-ng.org', '_blank')
|
||||
}
|
||||
|
||||
const goToLegacyWallet = () => {
|
||||
window.open('https://wallet.kaspanet.io', '_blank')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="welcome-page flex-center">
|
||||
<div class="welcome-card flex-center">
|
||||
<div class="welcome-box">
|
||||
<div class="header-section">
|
||||
<h2>Welcome to the New Wallet Experience</h2>
|
||||
<p>
|
||||
We've launched a new version of the Kaspa Wallet at
|
||||
<br />
|
||||
<span class="highlight"> https://kaspa-ng.org </span>
|
||||
</p>
|
||||
<ButtonCommon @click="goToNewWallet">
|
||||
Go to the new Kaspa NG Wallet
|
||||
</ButtonCommon>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<p>
|
||||
Already have funds on the old wallet?<br />
|
||||
You can still use <span class="highlight">https://wallet.kaspanet.io</span>
|
||||
</p>
|
||||
<ButtonCommon @click="goToLegacyWallet"> Continue on Legacy Wallet </ButtonCommon>
|
||||
<div class="note">Thank you for being a part of the Kaspa community!</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.welcome-page {
|
||||
min-height: 100vh;
|
||||
background-color: var(--vt-c-white);
|
||||
position: relative;
|
||||
|
||||
.welcome-card {
|
||||
background-color: var(--vt-c-white);
|
||||
box-shadow: 0 0 15px var(--vt-c-badge-caption-shadow);
|
||||
border-radius: 10px;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
.welcome-box {
|
||||
width: 100%;
|
||||
max-width: 700px;
|
||||
padding: 30px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
49
src/components/auth/OpenWalletComponent.vue
Normal file
49
src/components/auth/OpenWalletComponent.vue
Normal file
@ -0,0 +1,49 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ButtonCommon, FormCommon } from '@/components'
|
||||
|
||||
const password = ref('')
|
||||
|
||||
const handleOpenWallet = () => {}
|
||||
|
||||
const handleNewWallet = () => {}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="auth-container">
|
||||
<div class="auth-card">
|
||||
<div class="auth-card-header">
|
||||
<h2>Open Wallet</h2>
|
||||
</div>
|
||||
|
||||
<div class="auth-card-content">
|
||||
<div class="wallet-icon">
|
||||
<div class="icon-circle"></div>
|
||||
</div>
|
||||
|
||||
<div class="password-section">
|
||||
<FormCommon
|
||||
v-model="password"
|
||||
type="password"
|
||||
label="Unlock the wallet with your password:"
|
||||
placeholder="Enter your password"
|
||||
show-password-toggle
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="auth-button-group">
|
||||
<ButtonCommon class="auth-btn secondary" @click="handleNewWallet">
|
||||
NEW WALLET
|
||||
</ButtonCommon>
|
||||
|
||||
<ButtonCommon class="auth-btn primary" @click="handleOpenWallet">
|
||||
OPEN WALLET
|
||||
</ButtonCommon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
5
src/components/common/ButtonCommon.vue
Normal file
5
src/components/common/ButtonCommon.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<button class="btn-common">
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
152
src/components/common/FormCommon.vue
Normal file
152
src/components/common/FormCommon.vue
Normal file
@ -0,0 +1,152 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
import { IconCommon } from '../icon'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: { type: String, default: '' },
|
||||
type: { type: String, default: 'text' },
|
||||
placeholder: { type: String, default: '' },
|
||||
label: { type: String, default: '' },
|
||||
required: { type: Boolean, default: false },
|
||||
disabled: { type: Boolean, default: false },
|
||||
showPasswordToggle: { type: Boolean, default: false },
|
||||
error: { type: String, default: '' },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'focus', 'blur'])
|
||||
|
||||
const showPassword = ref(false)
|
||||
const isFocused = ref(false)
|
||||
|
||||
const inputType = computed(() => props.type)
|
||||
|
||||
const togglePassword = () => (showPassword.value = !showPassword.value)
|
||||
const handleInput = (e: Event) => emit('update:modelValue', (e.target as HTMLInputElement).value)
|
||||
const handleFocus = (e: FocusEvent) => {
|
||||
isFocused.value = true
|
||||
emit('focus', e)
|
||||
}
|
||||
const handleBlur = (e: FocusEvent) => {
|
||||
isFocused.value = false
|
||||
emit('blur', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="form-field">
|
||||
<label v-if="label" class="form-label">
|
||||
{{ label }}<span v-if="required" class="required-asterisk">*</span>
|
||||
</label>
|
||||
|
||||
<div class="input-container" :class="{ focused: isFocused, error, disabled }">
|
||||
<input
|
||||
v-bind="$attrs"
|
||||
:type="inputType"
|
||||
:value="modelValue"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:required="required"
|
||||
class="form-input"
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
/>
|
||||
|
||||
<button
|
||||
v-if="type === 'password' && showPasswordToggle"
|
||||
type="button"
|
||||
class="password-toggle flex-center"
|
||||
@click="togglePassword"
|
||||
>
|
||||
<IconCommon :size="20" :icon="showPassword ? 'eye-off' : 'eye'" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="error-message">{{ error }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.form-field {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--vt-c-black-bold);
|
||||
.required-asterisk {
|
||||
color: var(--vt-c-red-v3);
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.input-container {
|
||||
position: relative;
|
||||
border: 1px solid var(--vt-c-gray-6);
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
&.focused {
|
||||
border-color: var(--vt-c-main-color);
|
||||
box-shadow: var(--vt-input-shadow-focus);
|
||||
}
|
||||
&.error {
|
||||
border-color: var(--vt-c-red-v3);
|
||||
background-color: var()
|
||||
}
|
||||
&.disabled {
|
||||
background-color: var(--vt-c-gray-4);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 12px 40px 12px 12px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 14px;
|
||||
color: var(--vt-c-black-bold);
|
||||
outline: none;
|
||||
&::placeholder {
|
||||
color: var(--vt-c-gray-8);
|
||||
}
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
color: var(--vt-c-gray-8);
|
||||
}
|
||||
}
|
||||
|
||||
.password-toggle {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--vt-c-gray-8);
|
||||
padding: 4px;
|
||||
&:hover {
|
||||
color: var(--vt-c-black-bold);
|
||||
}
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.error-message {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: var(--vt-c-red-v3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@ -1,3 +0,0 @@
|
||||
import LayoutVue from './LayoutVue.vue'
|
||||
|
||||
export { LayoutVue }
|
||||
31
src/components/icon/IconCommon.vue
Normal file
31
src/components/icon/IconCommon.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import {
|
||||
EyeOutlined,
|
||||
EyeInvisibleOutlined,
|
||||
SearchOutlined,
|
||||
CloseOutlined,
|
||||
RightOutlined,
|
||||
LeftOutlined,
|
||||
} from '@ant-design/icons-vue'
|
||||
import type { IconProps } from '@/interface'
|
||||
|
||||
const props = withDefaults(defineProps<IconProps>(), {
|
||||
icon: '',
|
||||
})
|
||||
|
||||
const iconMap: Record<string, any> = {
|
||||
eye: EyeOutlined,
|
||||
'eye-off': EyeInvisibleOutlined,
|
||||
search: SearchOutlined,
|
||||
close: CloseOutlined,
|
||||
'arrow-right': RightOutlined,
|
||||
'arrow-left': LeftOutlined,
|
||||
}
|
||||
|
||||
const IconComponent = computed(() => iconMap[props.icon])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="IconComponent" :style="{ fontSize: size, color }" :class="props.class" />
|
||||
</template>
|
||||
1
src/components/icon/index.ts
Normal file
1
src/components/icon/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as IconCommon } from './IconCommon.vue'
|
||||
17
src/components/index.ts
Normal file
17
src/components/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import LayoutVue from './common/LayoutVue.vue'
|
||||
import ButtonCommon from './common/ButtonCommon.vue'
|
||||
import FormCommon from './common/FormCommon.vue'
|
||||
import OnboardingComponent from './auth/OnboardingComponent.vue'
|
||||
import OpenWalletComponent from './auth/OpenWalletComponent.vue'
|
||||
import CreateWalletComponent from './auth/CreateWalletComponent.vue'
|
||||
import { IconCommon } from './icon'
|
||||
|
||||
export {
|
||||
LayoutVue,
|
||||
ButtonCommon,
|
||||
FormCommon,
|
||||
OnboardingComponent,
|
||||
OpenWalletComponent,
|
||||
CreateWalletComponent,
|
||||
IconCommon,
|
||||
}
|
||||
9
src/interface/index.ts
Normal file
9
src/interface/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
interface Props {
|
||||
name?: string
|
||||
size?: number | string
|
||||
color?: string
|
||||
class?: string
|
||||
}
|
||||
export interface IconProps extends Props {
|
||||
icon: string
|
||||
}
|
||||
@ -34,4 +34,10 @@ export const routes: any = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/onboarding',
|
||||
name: 'onboarding',
|
||||
component: Page.Login,
|
||||
beforeEnter: ifNotAuthenticated,
|
||||
},
|
||||
]
|
||||
|
||||
8
src/views/Auth/LoginView.vue
Normal file
8
src/views/Auth/LoginView.vue
Normal file
@ -0,0 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { CreateWalletComponent } from '@/components'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
<CreateWalletComponent />
|
||||
</template>
|
||||
@ -1,2 +1,3 @@
|
||||
export const Home = () => import('@/views/Home/HomeView.vue')
|
||||
export const NotFound = () => import('@/views/NotFound/NotFoundView.vue')
|
||||
export const Login = () => import('@/views/Auth/LoginView.vue')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user