2017-09-25 03:22:33 +00:00
< template lang = "pug" >
2018-03-18 02:41:16 +00:00
v - app
2020-07-05 05:36:02 +00:00
. login ( : style = '`background-image: url(` + bgUrl + `);`' )
2020-07-03 23:36:33 +00:00
. login - sd
. d - flex
. login - logo
v - avatar ( tile , size = '34' )
v - img ( : src = 'logoUrl' )
. login - title
. text - h6 { { siteTitle } }
//-------------------------------------------------
//- PROVIDERS LIST
//-------------------------------------------------
template ( v - if = 'screen === `login` && strategies.length > 1' )
. login - subtitle . mt - 5
. text - subtitle - 1 Select Authentication Provider
. login - list
v - list . elevation - 1. radius - 7 ( nav )
v - list - item - group ( v - model = 'selectedStrategyKey' )
v - list - item (
2020-07-06 03:55:11 +00:00
v - for = '(stg, idx) of filteredStrategies'
2020-07-03 23:36:33 +00:00
: key = 'stg.key'
: value = 'stg.key'
: color = 'stg.strategy.color'
)
v - avatar . mr - 3 ( tile , size = '24' , v - html = 'stg.strategy.icon' )
span . text - none { { stg . displayName } }
//-------------------------------------------------
//- LOGIN FORM
//-------------------------------------------------
template ( v - if = 'screen === `login` && selectedStrategy.strategy.useForm' )
. login - subtitle
. text - subtitle - 1 Enter your credentials
. login - form
v - text - field (
solo
flat
prepend - inner - icon = 'mdi-clipboard-account'
background - color = 'white'
hide - details
ref = 'iptEmail'
v - model = 'username'
2020-07-19 17:20:43 +00:00
: placeholder = '$t(selectedStrategy.strategy.usernameLabel)'
2020-07-03 23:36:33 +00:00
)
v - text - field . mt - 2 (
solo
flat
prepend - inner - icon = 'mdi-form-textbox-password'
background - color = 'white'
hide - details
ref = 'iptPassword'
v - model = 'password'
: append - icon = 'hidePassword ? "mdi-eye-off" : "mdi-eye"'
@ click : append = '() => (hidePassword = !hidePassword)'
: type = 'hidePassword ? "password" : "text"'
: placeholder = '$t("auth:fields.password")'
@ keyup . enter = 'login'
2018-10-08 04:17:31 +00:00
)
2020-07-03 23:36:33 +00:00
v - btn . mt - 2. text - none (
width = '100%'
v - if = 'screen === "login"'
large
color = 'primary'
dark
@ click = 'login'
: loading = 'isLoading'
) { { $t ( 'auth:actions.login' ) } }
. text - center . mt - 5 ( v - if = 'screen === "login"' )
v - btn . text - none (
text
rounded
color = 'grey darken-3'
@ click . stop . prevent = 'forgotPassword'
href = '#forgot'
) : . caption { { $t ( 'auth:forgotPasswordLink' ) } }
v - btn . text - none (
v - if = 'screen === "login" && selectedStrategyKey === `local` && selectedStrategy.selfRegistration'
color = 'indigo darken-2'
text
rounded
href = '/register'
) : . caption { { $t ( 'auth:switchToRegister.link' ) } }
//- .login-main
//- v-container(grid-list-lg, fluid)
//- v-row(no-gutters)
//- v-col(cols='12', xl='4')
//- transition(name='fadeUp')
//- v-card.elevation-5(v-show='isShown', light)
//- v-toolbar(color='indigo', flat, dense, dark)
//- v-spacer
//- .subheading(v-if='screen === "tfa"') {{ $t('auth:tfa.subtitle') }}
//- .subheading(v-if='screen === "changePwd"') {{ $t('auth:changePwd.subtitle') }}
//- .subheading(v-else-if='selectedStrategy.key !== "local"') {{ $t('auth:loginUsingStrategy', { strategy: selectedStrategy.title, interpolation: { escapeValue: false } }) }}
//- .subheading(v-else) {{ $t('auth:loginRequired') }}
//- v-spacer
//- v-card-text.text-center
//- h1.display-1.indigo--text.py-2 {{ siteTitle }}
//- template(v-if='screen === "login"')
//- v-text-field.mt-3(
//- solo
//- flat
//- prepend-icon='mdi-clipboard-account'
//- background-color='grey lighten-4'
//- hide-details
//- ref='iptEmail'
//- v-model='username'
//- :placeholder='$t("auth:fields.emailUser")'
//- )
//- v-text-field.mt-2(
//- solo
//- flat
//- prepend-icon='mdi-textbox-password'
//- background-color='grey lighten-4'
//- hide-details
//- ref='iptPassword'
//- v-model='password'
//- :append-icon='hidePassword ? "mdi-eye-off" : "mdi-eye"'
//- @click:append='() => (hidePassword = !hidePassword)'
//- :type='hidePassword ? "password" : "text"'
//- :placeholder='$t("auth:fields.password")'
//- @keyup.enter='login'
//- )
//- template(v-else-if='screen === "tfa"')
//- .body-2 Enter the security code generated from your trusted device:
//- v-text-field.centered.mt-2(
//- solo
//- flat
//- background-color='grey lighten-4'
//- hide-details
//- ref='iptTFA'
//- v-model='securityCode'
//- :placeholder='$t("auth:tfa.placeholder")'
//- @keyup.enter='verifySecurityCode'
//- )
//- template(v-else-if='screen === "changePwd"')
//- .body-2 {{$t('auth:changePwd.instructions')}}
//- v-text-field.mt-2(
//- type='password'
//- solo
//- flat
//- background-color='grey lighten-4'
//- hide-details
//- ref='iptNewPassword'
//- v-model='newPassword'
//- :placeholder='$t(`auth:changePwd.newPasswordPlaceholder`)'
//- )
//- v-text-field.mt-2(
//- type='password'
//- solo
//- flat
//- background-color='grey lighten-4'
//- hide-details
//- v-model='newPasswordVerify'
//- :placeholder='$t(`auth:changePwd.newPasswordVerifyPlaceholder`)'
//- @keyup.enter='changePassword'
//- )
//- template(v-else-if='screen === "forgot"')
//- .body-2 {{ $t('auth:forgotPasswordSubtitle') }}
//- v-text-field.mt-3(
//- solo
//- flat
//- prepend-icon='mdi-email'
//- background-color='grey lighten-4'
//- hide-details
//- ref='iptEmailForgot'
//- v-model='username'
//- :placeholder='$t("auth:fields.email")'
//- )
//- v-card-actions.pb-4
//- v-spacer
//- v-btn(
//- width='100%'
//- max-width='250px'
//- v-if='screen === "login"'
//- large
//- color='primary'
//- dark
//- @click='login'
//- rounded
//- :loading='isLoading'
//- ) {{ $t('auth:actions.login') }}
//- v-btn(
//- width='100%'
//- max-width='250px'
//- v-else-if='screen === "tfa"'
//- large
//- color='primary'
//- dark
//- @click='verifySecurityCode'
//- rounded
//- :loading='isLoading'
//- ) {{ $t('auth:tfa.verifyToken') }}
//- v-btn(
//- width='100%'
//- max-width='250px'
//- v-else-if='screen === "changePwd"'
//- large
//- color='primary'
//- dark
//- @click='changePassword'
//- rounded
//- :loading='isLoading'
//- ) {{ $t('auth:changePwd.proceed') }}
//- v-btn(
//- width='100%'
//- max-width='250px'
//- v-else-if='screen === "forgot"'
//- large
//- color='primary'
//- dark
//- @click='forgotPasswordSubmit'
//- rounded
//- :loading='isLoading'
//- ) {{ $t('auth:sendResetPassword') }}
//- v-spacer
//- v-card-actions.pb-3(v-if='screen === "login" && selectedStrategy.key === "local"')
//- v-spacer
//- a.caption(@click.stop.prevent='forgotPassword', href='#forgot') {{ $t('auth:forgotPasswordLink') }}
//- v-spacer
//- v-card-actions.pb-3(v-else-if='screen === "forgot"')
//- v-spacer
//- a.caption(@click.stop.prevent='screen = `login`', href='#cancelforgot') {{ $t('auth:forgotPasswordCancel') }}
//- v-spacer
2018-12-17 05:51:52 +00:00
loader ( v - model = 'isLoading' , : color = 'loaderColor' , : title = 'loaderTitle' , : subtitle = '$t(`auth:pleaseWait`)' )
2019-04-22 04:44:24 +00:00
notify
2017-09-25 03:22:33 +00:00
< / template >
< script >
2018-03-25 02:35:47 +00:00
/* global siteConfig */
2017-10-30 01:36:05 +00:00
2020-07-03 23:36:33 +00:00
// <span>Photo by <a href="https://unsplash.com/@isaacquesada?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Isaac Quesada</a> on <a href="/t/textures-patterns?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></span>
2018-03-09 05:33:43 +00:00
import _ from 'lodash'
2018-10-08 04:17:31 +00:00
import Cookies from 'js-cookie'
2020-07-03 23:36:33 +00:00
import gql from 'graphql-tag'
import { sync } from 'vuex-pathify'
2018-03-09 05:33:43 +00:00
2017-09-25 03:22:33 +00:00
export default {
2018-06-17 21:29:19 +00:00
i18nOptions : { namespaces : 'auth' } ,
2020-07-05 05:36:02 +00:00
props : {
bgUrl : {
type : String ,
default : ''
} ,
hideLocal : {
type : Boolean ,
default : false
}
} ,
2018-01-10 01:41:53 +00:00
data ( ) {
2017-09-25 03:22:33 +00:00
return {
error : false ,
2017-10-01 03:47:14 +00:00
strategies : [ ] ,
2020-07-06 03:55:11 +00:00
selectedStrategyKey : 'unselected' ,
selectedStrategy : { key : 'unselected' , strategy : { useForm : false } } ,
2018-01-10 01:41:53 +00:00
screen : 'login' ,
username : '' ,
password : '' ,
2018-03-18 02:41:16 +00:00
hidePassword : true ,
2018-01-10 01:41:53 +00:00
securityCode : '' ,
2019-08-25 02:19:35 +00:00
continuationToken : '' ,
2018-10-08 04:17:31 +00:00
isLoading : false ,
2018-12-17 05:51:52 +00:00
loaderColor : 'grey darken-4' ,
loaderTitle : 'Working...' ,
2019-08-25 02:19:35 +00:00
isShown : false ,
newPassword : '' ,
newPasswordVerify : ''
2017-09-25 03:22:33 +00:00
}
} ,
computed : {
2020-07-03 23:36:33 +00:00
activeModal : sync ( 'editor/activeModal' ) ,
2018-01-10 01:41:53 +00:00
siteTitle ( ) {
2017-09-25 03:22:33 +00:00
return siteConfig . title
2018-10-08 04:17:31 +00:00
} ,
isSocialShown ( ) {
return this . strategies . length > 1
2020-07-03 23:36:33 +00:00
} ,
2020-07-06 03:55:11 +00:00
logoUrl ( ) { return siteConfig . logoUrl } ,
filteredStrategies ( ) {
const qParams = new URLSearchParams ( window . location . search )
if ( this . hideLocal && ! qParams . has ( 'all' ) ) {
return _ . reject ( this . strategies , [ 'key' , 'local' ] )
} else {
return this . strategies
}
}
2018-10-08 04:17:31 +00:00
} ,
watch : {
2020-07-06 03:55:11 +00:00
filteredStrategies ( newValue , oldValue ) {
if ( _ . head ( newValue ) . strategy . useForm ) {
this . selectedStrategyKey = _ . head ( newValue ) . key
}
2020-07-03 23:36:33 +00:00
} ,
selectedStrategyKey ( newValue , oldValue ) {
this . selectedStrategy = _ . find ( this . strategies , [ 'key' , newValue ] )
this . screen = 'login'
if ( ! this . selectedStrategy . strategy . useForm ) {
this . isLoading = true
window . location . assign ( '/login/' + newValue )
} else {
this . $nextTick ( ( ) => {
this . $refs . iptEmail . focus ( )
} )
}
2017-09-25 03:22:33 +00:00
}
2017-09-30 02:32:43 +00:00
} ,
2018-02-03 21:48:25 +00:00
mounted ( ) {
2018-10-08 04:17:31 +00:00
this . isShown = true
this . $nextTick ( ( ) => {
2020-07-03 23:36:33 +00:00
// this.$refs.iptEmail.focus()
2018-10-08 04:17:31 +00:00
} )
2018-02-03 21:48:25 +00:00
} ,
2017-09-25 03:22:33 +00:00
methods : {
2018-06-17 15:12:11 +00:00
/ * *
* LOGIN
* /
async login ( ) {
2018-01-10 01:41:53 +00:00
if ( this . username . length < 2 ) {
2018-04-14 19:22:45 +00:00
this . $store . commit ( 'showNotification' , {
style : 'red' ,
2018-12-17 05:51:52 +00:00
message : this . $t ( 'auth:invalidEmailUsername' ) ,
2019-08-25 02:19:35 +00:00
icon : 'alert'
2018-01-10 01:41:53 +00:00
} )
this . $refs . iptEmail . focus ( )
} else if ( this . password . length < 2 ) {
2018-04-14 19:22:45 +00:00
this . $store . commit ( 'showNotification' , {
style : 'red' ,
2018-12-17 05:51:52 +00:00
message : this . $t ( 'auth:invalidPassword' ) ,
2019-08-25 02:19:35 +00:00
icon : 'alert'
2018-01-10 01:41:53 +00:00
} )
this . $refs . iptPassword . focus ( )
} else {
2018-12-17 05:51:52 +00:00
this . loaderColor = 'grey darken-4'
this . loaderTitle = this . $t ( 'auth:signingIn' )
2018-01-10 01:41:53 +00:00
this . isLoading = true
2018-06-17 15:12:11 +00:00
try {
let resp = await this . $apollo . mutate ( {
2020-07-03 23:36:33 +00:00
mutation : gql `
mutation ( $username : String ! , $password : String ! , $strategy : String ! ) {
authentication {
login ( username : $username , password : $password , strategy : $strategy ) {
responseResult {
succeeded
errorCode
slug
message
}
jwt
mustChangePwd
mustProvideTFA
continuationToken
}
}
}
` ,
2018-06-17 15:12:11 +00:00
variables : {
username : this . username ,
password : this . password ,
2018-10-08 04:17:31 +00:00
strategy : this . selectedStrategy . key
2018-06-17 15:12:11 +00:00
}
} )
2018-03-10 05:58:04 +00:00
if ( _ . has ( resp , 'data.authentication.login' ) ) {
let respObj = _ . get ( resp , 'data.authentication.login' , { } )
2018-04-14 19:22:45 +00:00
if ( respObj . responseResult . succeeded === true ) {
2019-08-25 02:19:35 +00:00
this . continuationToken = respObj . continuationToken
if ( respObj . mustChangePwd === true ) {
this . screen = 'changePwd'
this . $nextTick ( ( ) => {
this . $refs . iptNewPassword . focus ( )
} )
this . isLoading = false
} else if ( respObj . mustProvideTFA === true ) {
2018-01-10 01:41:53 +00:00
this . screen = 'tfa'
this . securityCode = ''
this . $nextTick ( ( ) => {
this . $refs . iptTFA . focus ( )
} )
2018-12-17 05:51:52 +00:00
this . isLoading = false
2018-01-10 01:41:53 +00:00
} else {
2018-12-17 05:51:52 +00:00
this . loaderColor = 'green darken-1'
this . loaderTitle = this . $t ( 'auth:loginSuccess' )
2018-10-08 04:17:31 +00:00
Cookies . set ( 'jwt' , respObj . jwt , { expires : 365 } )
2018-06-04 04:41:29 +00:00
_ . delay ( ( ) => {
2020-02-07 19:51:11 +00:00
const loginRedirect = Cookies . get ( 'loginRedirect' )
if ( loginRedirect ) {
Cookies . remove ( 'loginRedirect' )
window . location . replace ( loginRedirect )
} else {
window . location . replace ( '/' )
}
2018-06-04 04:41:29 +00:00
} , 1000 )
2018-01-10 01:41:53 +00:00
}
} else {
2018-04-14 19:22:45 +00:00
throw new Error ( respObj . responseResult . message )
2018-01-10 01:41:53 +00:00
}
} else {
2018-12-17 05:51:52 +00:00
throw new Error ( this . $t ( 'auth:genericError' ) )
2018-01-10 01:41:53 +00:00
}
2018-06-17 15:12:11 +00:00
} catch ( err ) {
2018-01-10 01:41:53 +00:00
console . error ( err )
2018-04-14 19:22:45 +00:00
this . $store . commit ( 'showNotification' , {
style : 'red' ,
message : err . message ,
2019-08-25 02:19:35 +00:00
icon : 'alert'
2018-01-10 01:41:53 +00:00
} )
this . isLoading = false
2018-06-17 15:12:11 +00:00
}
2018-01-10 01:41:53 +00:00
}
} ,
2018-06-17 15:12:11 +00:00
/ * *
* VERIFY TFA CODE
* /
2018-01-10 01:41:53 +00:00
verifySecurityCode ( ) {
if ( this . securityCode . length !== 6 ) {
2018-04-14 19:22:45 +00:00
this . $store . commit ( 'showNotification' , {
style : 'red' ,
message : 'Enter a valid security code.' ,
icon : 'warning'
2018-01-10 01:41:53 +00:00
} )
this . $refs . iptTFA . focus ( )
} else {
this . isLoading = true
2018-03-25 02:35:47 +00:00
this . $apollo . mutate ( {
2020-07-03 23:36:33 +00:00
mutation : gql `
{
2020-07-04 00:09:22 +00:00
authentication {
activeStrategies {
key
}
}
2020-07-03 23:36:33 +00:00
}
` ,
2018-01-10 01:41:53 +00:00
variables : {
2019-08-25 02:19:35 +00:00
continuationToken : this . continuationToken ,
2018-01-10 01:41:53 +00:00
securityCode : this . securityCode
}
} ) . then ( resp => {
2018-03-10 05:58:04 +00:00
if ( _ . has ( resp , 'data.authentication.loginTFA' ) ) {
let respObj = _ . get ( resp , 'data.authentication.loginTFA' , { } )
2018-04-14 19:22:45 +00:00
if ( respObj . responseResult . succeeded === true ) {
this . $store . commit ( 'showNotification' , {
message : 'Login successful!' ,
2018-01-10 01:41:53 +00:00
style : 'success' ,
2018-04-14 19:22:45 +00:00
icon : 'check'
2018-01-10 01:41:53 +00:00
} )
2018-06-04 04:41:29 +00:00
_ . delay ( ( ) => {
window . location . replace ( '/' ) // TEMPORARY - USE RETURNURL
} , 1000 )
2018-01-10 01:41:53 +00:00
this . isLoading = false
} else {
2018-04-14 19:22:45 +00:00
throw new Error ( respObj . responseResult . message )
2018-01-10 01:41:53 +00:00
}
} else {
2018-12-17 05:51:52 +00:00
throw new Error ( this . $t ( 'auth:genericError' ) )
2018-01-10 01:41:53 +00:00
}
} ) . catch ( err => {
console . error ( err )
2018-04-14 19:22:45 +00:00
this . $store . commit ( 'showNotification' , {
style : 'red' ,
message : err . message ,
2019-08-25 02:19:35 +00:00
icon : 'alert'
2018-01-10 01:41:53 +00:00
} )
this . isLoading = false
} )
}
2018-12-21 04:02:17 +00:00
} ,
2019-08-25 02:19:35 +00:00
/ * *
* CHANGE PASSWORD
* /
async changePassword ( ) {
this . loaderColor = 'grey darken-4'
this . loaderTitle = this . $t ( 'auth:changePwd.loading' )
this . isLoading = true
const resp = await this . $apollo . mutate ( {
2020-07-03 23:36:33 +00:00
mutation : gql `
{
2020-07-04 00:09:22 +00:00
authentication {
activeStrategies {
key
}
}
2020-07-03 23:36:33 +00:00
}
` ,
2019-08-25 02:19:35 +00:00
variables : {
continuationToken : this . continuationToken ,
newPassword : this . newPassword
}
} )
if ( _ . get ( resp , 'data.authentication.loginChangePassword.responseResult.succeeded' , false ) === true ) {
this . loaderColor = 'green darken-1'
this . loaderTitle = this . $t ( 'auth:loginSuccess' )
Cookies . set ( 'jwt' , _ . get ( resp , 'data.authentication.loginChangePassword.jwt' , '' ) , { expires : 365 } )
_ . delay ( ( ) => {
window . location . replace ( '/' ) // TEMPORARY - USE RETURNURL
} , 1000 )
} else {
this . $store . commit ( 'showNotification' , {
style : 'red' ,
message : _ . get ( resp , 'data.authentication.loginChangePassword.responseResult.message' , false ) ,
icon : 'alert'
} )
this . isLoading = false
}
} ,
/ * *
* SWITCH TO FORGOT PASSWORD SCREEN
* /
forgotPassword ( ) {
2018-12-21 04:02:17 +00:00
this . screen = 'forgot'
this . $nextTick ( ( ) => {
this . $refs . iptEmailForgot . focus ( )
} )
} ,
2019-08-25 02:19:35 +00:00
/ * *
* FORGOT PASSWORD SUBMIT
* /
async forgotPasswordSubmit ( ) {
2018-12-21 04:02:17 +00:00
this . $store . commit ( 'showNotification' , {
style : 'pink' ,
message : 'Coming soon!' ,
2019-08-25 02:19:35 +00:00
icon : 'ferry'
2018-12-21 04:02:17 +00:00
} )
2017-09-25 03:22:33 +00:00
}
2018-06-17 15:12:11 +00:00
} ,
apollo : {
strategies : {
2020-07-03 23:36:33 +00:00
query : gql `
{
authentication {
activeStrategies {
key
strategy {
key
logo
color
icon
useForm
2020-07-19 17:20:43 +00:00
usernameLabel
2020-07-03 23:36:33 +00:00
}
displayName
order
selfRegistration
}
}
}
` ,
update : ( data ) => _ . sortBy ( data . authentication . activeStrategies , [ 'order' ] ) ,
2018-06-17 15:12:11 +00:00
watchLoading ( isLoading ) {
this . $store . commit ( ` loading ${ isLoading ? 'Start' : 'Stop' } ` , 'login-strategies-refresh' )
}
}
2017-09-25 03:22:33 +00:00
}
}
< / script >
2018-01-21 22:54:43 +00:00
< style lang = "scss" >
. login {
2020-07-05 05:36:02 +00:00
// background-image: url('/_assets/img/splash/1.jpg');
background - color : mc ( 'grey' , '900' ) ;
2020-07-03 23:36:33 +00:00
background - size : cover ;
background - position : center center ;
2018-01-21 22:54:43 +00:00
width : 100 % ;
height : 100 % ;
2020-07-03 23:36:33 +00:00
& - sd {
background - color : rgba ( 255 , 255 , 255 , .8 ) ;
backdrop - filter : blur ( 10 px ) ;
- webkit - backdrop - filter : blur ( 10 px ) ;
border - left : 1 px solid rgba ( 255 , 255 , 255 , .85 ) ;
border - right : 1 px solid rgba ( 255 , 255 , 255 , .85 ) ;
width : 450 px ;
height : 100 % ;
margin - left : 5 vw ;
@ at - root . no - backdropfilter & {
background - color : rgba ( 255 , 255 , 255 , .95 ) ;
2018-01-21 22:54:43 +00:00
}
2020-07-03 23:36:33 +00:00
@ include until ( $tablet ) {
margin - left : 0 ;
width : 100 % ;
2018-01-21 22:54:43 +00:00
}
}
2020-07-03 23:36:33 +00:00
& - logo {
padding : 12 px 0 0 12 px ;
width : 58 px ;
height : 58 px ;
background - color : # 222 ;
margin - left : 12 px ;
border - bottom - left - radius : 7 px ;
border - bottom - right - radius : 7 px ;
2018-01-21 22:54:43 +00:00
}
2020-07-03 23:36:33 +00:00
& - title {
height : 58 px ;
padding - left : 12 px ;
2018-01-21 22:54:43 +00:00
display : flex ;
2020-07-03 23:36:33 +00:00
align - items : center ;
text - shadow : .5 px .5 px # FFF ;
2018-01-21 22:54:43 +00:00
}
2020-07-03 23:36:33 +00:00
& - subtitle {
padding : 24 px 12 px 12 px 12 px ;
color : # 111 ;
font - weight : 500 ;
text - shadow : 1 px 1 px rgba ( 255 , 255 , 255 , .5 ) ;
background - image : linear - gradient ( to bottom , rgba ( 0 , 0 , 0 , 0 ) , rgba ( 0 , 0 , 0 , .15 ) ) ;
text - align : center ;
border - bottom : 1 px solid rgba ( 0 , 0 , 0 , .3 ) ;
2018-10-08 04:17:31 +00:00
}
2018-01-21 22:54:43 +00:00
2020-07-03 23:36:33 +00:00
& - list {
border - top : 1 px solid rgba ( 255 , 255 , 255 , .85 ) ;
padding : 12 px ;
}
& - form {
padding : 12 px ;
border - top : 1 px solid rgba ( 255 , 255 , 255 , .85 ) ;
}
& - main {
flex : 1 0 100 vw ;
height : 100 vh ;
2018-01-21 22:54:43 +00:00
}
}
< / style >