diff options
| author | sowgro <tpoke.ferrari@gmail.com> | 2025-03-30 16:26:12 -0400 | 
|---|---|---|
| committer | sowgro <tpoke.ferrari@gmail.com> | 2025-03-30 16:26:12 -0400 | 
| commit | 27b2b418cc330e37f5802a81d678dd27259ee1e0 (patch) | |
| tree | 893231478cb132731546ef7eb6b5c94d0c748091 /ufund-ui/src | |
| parent | e1eb3f16e10042c2539b56d6c2d2e33f07abf7d9 (diff) | |
| parent | c6bbb29f42eaea7d0c8aebdb7b95be0287cbf4f9 (diff) | |
| download | JellySolutions-27b2b418cc330e37f5802a81d678dd27259ee1e0.tar.gz JellySolutions-27b2b418cc330e37f5802a81d678dd27259ee1e0.tar.bz2 JellySolutions-27b2b418cc330e37f5802a81d678dd27259ee1e0.zip  | |
Merge branch 'main' into css
# Conflicts:
#	ufund-ui/src/app/app-routing.module.ts
#	ufund-ui/src/app/app.module.ts
#	ufund-ui/src/app/components/login/login.component.html
Diffstat (limited to '')
| -rw-r--r-- | ufund-ui/src/app/app-routing.module.ts | 4 | ||||
| -rw-r--r-- | ufund-ui/src/app/app.module.ts | 2 | ||||
| -rw-r--r-- | ufund-ui/src/app/components/login/login.component.html | 3 | ||||
| -rw-r--r-- | ufund-ui/src/app/components/signup/signup.component.css | 47 | ||||
| -rw-r--r-- | ufund-ui/src/app/components/signup/signup.component.html | 26 | ||||
| -rw-r--r-- | ufund-ui/src/app/components/signup/signup.component.ts | 165 | 
6 files changed, 244 insertions, 3 deletions
diff --git a/ufund-ui/src/app/app-routing.module.ts b/ufund-ui/src/app/app-routing.module.ts index c83db8a..89b6f67 100644 --- a/ufund-ui/src/app/app-routing.module.ts +++ b/ufund-ui/src/app/app-routing.module.ts @@ -6,6 +6,7 @@ import {LoginComponent} from './components/login/login.component';  import {HomePageComponent} from './components/home-page/home-page.component';  import {FundingBasketComponent} from './components/funding-basket/funding-basket.component';  import {NeedPageComponent} from './components/need-page/need-page.component'; +import {SignupComponent} from './components/signup/signup.component';  const routes: Routes = [      { path: '',          component: HomePageComponent,      title: "Home | JS"      }, @@ -13,7 +14,8 @@ const routes: Routes = [      { path: 'cupboard',  component: CupboardComponent,      title: "Cupboard | JS"  },      { path: 'dashboard', component: DashboardComponent,     title: "Dashboard | JS" },      { path: 'basket',    component: FundingBasketComponent, title: "Basket | JS"    }, -    { path: 'need/:id',  component: NeedPageComponent,      title: "Need | JS"      } +    { path: 'need/:id',  component: NeedPageComponent,      title: "Need | JS"      }, +    { path: 'signup',    component: SignupComponent,        title: "Signup | JS"    },  ];  @NgModule({ diff --git a/ufund-ui/src/app/app.module.ts b/ufund-ui/src/app/app.module.ts index f1d6184..ea7e6ad 100644 --- a/ufund-ui/src/app/app.module.ts +++ b/ufund-ui/src/app/app.module.ts @@ -15,6 +15,7 @@ import {DashboardComponent} from './components/dashboard/dashboard.component';  import {CommonModule} from '@angular/common';  import {LoginComponent} from './components/login/login.component';  import { MiniNeedListComponent } from './components/mini-need-list/mini-need-list.component'; +import { SignupComponent } from './components/signup/signup.component';  @NgModule({      declarations: [ @@ -26,6 +27,7 @@ import { MiniNeedListComponent } from './components/mini-need-list/mini-need-lis          NeedListComponent,          DashboardComponent,          LoginComponent, +        SignupComponent,          MiniNeedListComponent,      ],      imports: [ diff --git a/ufund-ui/src/app/components/login/login.component.html b/ufund-ui/src/app/components/login/login.component.html index e04ec23..743b1b3 100644 --- a/ufund-ui/src/app/components/login/login.component.html +++ b/ufund-ui/src/app/components/login/login.component.html @@ -5,8 +5,7 @@      <input placeholder="Password" type="password" #password>      <button type="button" (click)="login(username.value, password.value)">Login</button>      <div> -        New? <a href="/">Create an account</a> +        New? <a routerLink="/signup">Create an account</a>      </div> -<!--    <button type="button" (click)="signup(username.value, password.value)">Create Account</button>-->      <span *ngIf="statusText">{{statusText | async}}</span>  </div> diff --git a/ufund-ui/src/app/components/signup/signup.component.css b/ufund-ui/src/app/components/signup/signup.component.css new file mode 100644 index 0000000..f286cf9 --- /dev/null +++ b/ufund-ui/src/app/components/signup/signup.component.css @@ -0,0 +1,47 @@ +:host { +    display: flex; +    flex-direction: column; +    max-width: 300px; +    gap: 10px; + +    & > div { +        display: flex; +        flex-direction: column; +    } +} + +.border { +    border-style: solid; +    border-width: 1px; +    padding: 10px; +    margin: 10px; +    position: absolute; +    background-color: white; +    box-shadow: 0 0 10px 10px black; +} + +#bar { +    height: 5px; +    width: 100%; +    appearance: none; +    overflow: hidden; +    /*margin-top: -5px;*/ +} + +#bar::-webkit-progress-bar { +    background-color: lightgray; +    transition: width 0.5s ease-in-out, background-color 0.5s ease-in-out; +} + +.color0::-webkit-progress-value { background: rgba(255, 0 ,0, 1); transition: background-color 0.5s ease-in-out, width 0.5s ease-in-out; } +.color1::-webkit-progress-value { background: rgba(255, 0 ,0, 1); transition: background-color 0.5s ease-in-out, width 0.5s ease-in-out; } +.color2::-webkit-progress-value { background: rgba(255, 165, 0, 1); transition: background-color 0.5s ease-in-out, width 0.5s ease-in-out; } +.color3::-webkit-progress-value { background: rgba(255, 255, 0, 1); transition: background-color 0.5s ease-in-out, width 0.5s ease-in-out; } +.color4::-webkit-progress-value { background: rgba(173, 255, 47, 1); transition: background-color 0.5s ease-in-out, width 0.5s ease-in-out; } +.color5::-webkit-progress-value { background: rgba(50, 205, 50, 1); transition: background-color 0.5s ease-in-out, width 0.5s ease-in-out; } +.color6::-webkit-progress-value { background: rgba(0, 128, 0, 1); transition: background-color 0.5s ease-in-out, width 0.5s ease-in-out; } + +#requirement2, #statusText, #passwordStatusText, #usernameStatusText { +    color: red; +} + diff --git a/ufund-ui/src/app/components/signup/signup.component.html b/ufund-ui/src/app/components/signup/signup.component.html new file mode 100644 index 0000000..ebedc2a --- /dev/null +++ b/ufund-ui/src/app/components/signup/signup.component.html @@ -0,0 +1,26 @@ +<p>Signup:</p> +<div> +    <input placeholder="Username" type="text" (input)="validate(username.value, confirmPass.value, password.value)" #username> +    <span *ngIf="usernameStatusText">{{usernameStatusText | async}}</span> +</div> + +<div> +    <input placeholder="Password" type="password" (input)="validate(username.value, confirmPass.value, password.value)" #password> +    <progress [ngClass]="'color' + strength.getValue()" id="bar" [value]="strength | async" max="6">  </progress> +    <span *ngIf="passwordStatusText">{{passwordStatusText | async}}</span> + +    <span *ngFor="let requirement of Object.values(passwordRequirements)"> +        <span *ngIf="passwordRequirements" [style.color]="requirement.value ? 'green' : 'red'"> {{requirement.title}} </span> +    </span> +</div> + +<div> +    <input placeholder="Confirm password" type="password" (input)="validate(username.value, confirmPass.value, password.value)" #confirmPass> +    <span *ngIf="confirmPassStatusText">{{confirmPassStatusText | async}}</span> +</div> + +<div> +    <button [disabled]="!(ableToCreateAccount | async)" (click)="signup(username.value, password.value)">Create Account</button> +    <span *ngIf="showSuccessMessage | async">Account created <a routerLink="/login">Proceed to login</a></span> +    <span *ngIf="statusText | async">{{statusText | async}}</span> +</div> diff --git a/ufund-ui/src/app/components/signup/signup.component.ts b/ufund-ui/src/app/components/signup/signup.component.ts new file mode 100644 index 0000000..3b43287 --- /dev/null +++ b/ufund-ui/src/app/components/signup/signup.component.ts @@ -0,0 +1,165 @@ +import {Component} from '@angular/core'; +import {UsersService} from '../../services/users.service'; +import {Router} from '@angular/router'; +import {BehaviorSubject} from 'rxjs'; + +class PasswordRequirements { +    sixLong:    {title: string, value: boolean} = {title: 'Is 6 characters or longer' , value: false} +    twelveLong: {title: string, value: boolean} = {title: 'Is 12 characters or longer', value: false} +    lowercase:  {title: string, value: boolean} = {title: 'Includes lowercase letter' , value: false} +    uppercase:  {title: string, value: boolean} = {title: 'Includes uppercase letter' , value: false} +    number:     {title: string, value: boolean} = {title: 'Includes number'           , value: false} +    symbol:     {title: string, value: boolean} = {title: 'Includes symbol'           , value: false} +} + +@Component({ +  selector: 'app-signup', +  standalone: false, +  templateUrl: './signup.component.html', +  styleUrl: './signup.component.css' +}) + +export class SignupComponent { + +    protected passwordStatusText = new BehaviorSubject("") +    protected confirmPassStatusText = new BehaviorSubject("") +    protected usernameStatusText = new BehaviorSubject("") +    protected showSuccessMessage = new BehaviorSubject(false) +    protected passwordStrongEnough = new BehaviorSubject(false) +    protected ableToCreateAccount = new BehaviorSubject(false) +    protected passwordRequirements: PasswordRequirements = new PasswordRequirements() +    protected strength = new BehaviorSubject(0) +    protected statusText = new BehaviorSubject(""); + +    constructor( +        protected usersService: UsersService, +        protected router: Router, +    ) {} + +    signup(username: string | null, password: string | null) { +        console.log(`attempting to sign up with ${username} ${password}`) +        if (!username || !password) { +            return; +        } + +        this.usersService.createUser(username, password).then(() => { +            this.showSuccessMessage.next(true); +        }).catch(ex => { +            this.statusText.next("Unable to create account: " + friendlyHttpStatus[ex.status]) +            console.log(ex) +        }) +    } + +    validate(username: string, passConfirm:string, password: string) { +        this.confirmPassStatusText.next("") +        this.usernameStatusText.next("") +        this.checkPasswordStrength(password); + +        if (username === "") { +            this.usernameStatusText.next("Username field can't be blank") +        } + +        if (!(password === passConfirm) && !!passConfirm) { +            this.confirmPassStatusText.next("Passwords don't match") +        } +        this.ableToCreateAccount.next(this.passwordStrongEnough.getValue() && password === passConfirm && !!username) +    } + +    checkPasswordStrength(password: string) { +        this.strength.next(0) +        this.passwordRequirements = new PasswordRequirements() +        this.passwordStrongEnough.next(false) + +        if (password.match(/[^!-~]/g)) { +            this.passwordStatusText.next("Invalid characters") + +            return +        } + +        if (password.length > 6) { +            this.passwordRequirements.sixLong.value = true +        } +        if (password.length > 12) { +            this.passwordRequirements.twelveLong.value = true +        } +        if (password.match(/[a-z]/g)) { +            this.passwordRequirements.lowercase.value = true +        } +        if (password.match(/[A-Z]/g)) { +            this.passwordRequirements.uppercase.value = true +        } +        if (password.match(/[0-9]/g)) { +            this.passwordRequirements.number.value = true +        } +        if (password.match(/[^A-Za-z0-9]/g)) { +            this.passwordRequirements.symbol.value = true +        } + +        let strength = 0 +        Object.values(this.passwordRequirements).forEach(k => { +            k.value && strength++ +        }) + +        if (strength >= 5) { +            this.passwordStrongEnough.next(true) +            this.passwordStatusText.next("") +        } else if (strength == 0) { +            this.passwordStatusText.next("") +        } else { +            this.passwordStatusText.next("Password does not meet requirements") +        } + +        this.strength.next(strength) +    } + +    getColor() { +        return `rgba(${(this.strength.getValue()/7) * 255}, ${255 - (this.strength.getValue()/7) * 255}, 0)` +    } + +    protected readonly length = length; +    protected readonly Object = Object; +} + +let friendlyHttpStatus: {[key: number]: string} = { +    200: 'OK', +    201: 'Created', +    202: 'Accepted', +    203: 'Non-Authoritative Information', +    204: 'No Content', +    205: 'Reset Content', +    206: 'Partial Content', +    300: 'Multiple Choices', +    301: 'Moved Permanently', +    302: 'Found', +    303: 'See Other', +    304: 'Not Modified', +    305: 'Use Proxy', +    306: 'Unused', +    307: 'Temporary Redirect', +    400: 'Bad Request', +    401: 'Unauthorized', +    402: 'Payment Required', +    403: 'Forbidden', +    404: 'Not Found', +    405: 'Method Not Allowed', +    406: 'Not Acceptable', +    407: 'Proxy Authentication Required', +    408: 'Request Timeout', +    409: 'Conflict', +    410: 'Gone', +    411: 'Length Required', +    412: 'Precondition Required', +    413: 'Request Entry Too Large', +    414: 'Request-URI Too Long', +    415: 'Unsupported Media Type', +    416: 'Requested Range Not Satisfiable', +    417: 'Expectation Failed', +    418: 'I\'m a teapot', +    429: 'Too Many Requests', +    500: 'Internal Server Error', +    501: 'Not Implemented', +    502: 'Bad Gateway', +    503: 'Service Unavailable', +    504: 'Gateway Timeout', +    505: 'HTTP Version Not Supported', +};  | 
