diff --git a/backend/src/main/java/com/personal/patient/account/configs/SecurityConfig.java b/backend/src/main/java/com/personal/patient/account/configs/SecurityConfig.java index c780708..ab4408f 100644 --- a/backend/src/main/java/com/personal/patient/account/configs/SecurityConfig.java +++ b/backend/src/main/java/com/personal/patient/account/configs/SecurityConfig.java @@ -57,7 +57,6 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception{ .antMatchers("/documents/passport/passport-file").authenticated() .antMatchers("/documents/passport/passport-file/**").authenticated() .antMatchers("/profile").authenticated() - .antMatchers("/profile").authenticated() .antMatchers("/admin").hasRole("ADMIN") .anyRequest().permitAll() .and() diff --git a/backend/src/main/java/com/personal/patient/account/controllers/AuthController.java b/backend/src/main/java/com/personal/patient/account/controllers/AuthController.java index f6ac67a..1c5fb5b 100644 --- a/backend/src/main/java/com/personal/patient/account/controllers/AuthController.java +++ b/backend/src/main/java/com/personal/patient/account/controllers/AuthController.java @@ -4,7 +4,10 @@ import com.personal.patient.account.models.RegistrationUser; import com.personal.patient.account.service.AuthService; import lombok.RequiredArgsConstructor; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @@ -14,6 +17,11 @@ public class AuthController { private final AuthService authService; + @ExceptionHandler(DataIntegrityViolationException.class) + public ResponseEntity handleDataIntegrityViolationException(DataIntegrityViolationException ex) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Ошибка валидации: email не уникальный"); + } + @PostMapping("/auth") public ResponseEntity createAuthToken(@RequestBody JwtRequest authRequest){ return authService.createAuthToken(authRequest); diff --git a/backend/src/main/java/com/personal/patient/account/controllers/ProfileController.java b/backend/src/main/java/com/personal/patient/account/controllers/ProfileController.java index 4be6884..c06bb3f 100644 --- a/backend/src/main/java/com/personal/patient/account/controllers/ProfileController.java +++ b/backend/src/main/java/com/personal/patient/account/controllers/ProfileController.java @@ -2,6 +2,7 @@ import com.personal.patient.account.entities.User; import com.personal.patient.account.entities.Profile; +import com.personal.patient.account.exceptions.AlreadyExistException; import com.personal.patient.account.exceptions.NotFoundException; import com.personal.patient.account.models.ProfileRepresentation; import com.personal.patient.account.models.FullProfileRepresentation; @@ -25,12 +26,20 @@ protected ResponseEntity handleNotFoundException(NotFoundException ex) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage()); } + @ExceptionHandler(AlreadyExistException.class) + protected ResponseEntity handleAlreadyExistException(AlreadyExistException ex) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage()); + } + @PostMapping("") public ResponseEntity saveOrChangeProfile(@RequestBody ProfileRepresentation profileRequest, Principal principal){ - User user = userService.getUserByPrincipal(principal); - profileService.createOrChangeProfile(profileRequest, user); - return ResponseEntity.ok(new FullProfileRepresentation(profileRequest, user.getId())); + return ResponseEntity.ok(profileService.createProfile(profileRequest, principal)); + } + + @PutMapping("") + public ResponseEntity changeProfile(@RequestBody ProfileRepresentation profileRequest, Principal principal){ + return ResponseEntity.ok(profileService.changeProfile(profileRequest, principal)); } @GetMapping("") diff --git a/backend/src/main/java/com/personal/patient/account/exceptions/AlreadyExistException.java b/backend/src/main/java/com/personal/patient/account/exceptions/AlreadyExistException.java new file mode 100644 index 0000000..9d101e9 --- /dev/null +++ b/backend/src/main/java/com/personal/patient/account/exceptions/AlreadyExistException.java @@ -0,0 +1,7 @@ +package com.personal.patient.account.exceptions; + +public class AlreadyExistException extends RuntimeException { + public AlreadyExistException(String message) { + super(message); + } +} diff --git a/backend/src/main/java/com/personal/patient/account/models/CreatingPassportRequest.java b/backend/src/main/java/com/personal/patient/account/models/CreatingPassportRequest.java index 895f6be..1fca472 100644 --- a/backend/src/main/java/com/personal/patient/account/models/CreatingPassportRequest.java +++ b/backend/src/main/java/com/personal/patient/account/models/CreatingPassportRequest.java @@ -6,8 +6,10 @@ import lombok.Data; import lombok.NoArgsConstructor; +import java.text.SimpleDateFormat; import java.util.Date; + @Data @AllArgsConstructor @NoArgsConstructor @@ -18,7 +20,7 @@ public class CreatingPassportRequest { private PassportGender passportGender; - private Date dateOfBirth; + private String dateOfBirth; private String placeOfBirth; @@ -26,13 +28,12 @@ public class CreatingPassportRequest { private Integer number; - private Date dateOfIssue; + private String dateOfIssue; private Integer divisionCode; private String issuedBy; - //TO DO поменять убрать время public CreatingPassportRequest(Passport passport){ this.id = passport.getId(); @@ -40,7 +41,7 @@ public CreatingPassportRequest(Passport passport){ this.passportGender = passport.getPassportGender(); - this.dateOfBirth = passport.getDateOfBirth(); + this.dateOfBirth = parseDateToString(passport.getDateOfBirth()); this.placeOfBirth = passport.getPlaceOfBirth(); @@ -48,10 +49,15 @@ public CreatingPassportRequest(Passport passport){ this.number = passport.getNumber(); - this.dateOfIssue = passport.getDateOfIssue(); + this.dateOfIssue = parseDateToString(passport.getDateOfIssue()); this.divisionCode = passport.getDivisionCode(); this.issuedBy = passport.getIssuedBy(); } + + public String parseDateToString(Date date){ + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + return dateFormat.format(date); + } } diff --git a/backend/src/main/java/com/personal/patient/account/models/CreatingPassportResponse.java b/backend/src/main/java/com/personal/patient/account/models/CreatingPassportResponse.java index 45df720..3488175 100644 --- a/backend/src/main/java/com/personal/patient/account/models/CreatingPassportResponse.java +++ b/backend/src/main/java/com/personal/patient/account/models/CreatingPassportResponse.java @@ -1,10 +1,8 @@ package com.personal.patient.account.models; -import com.personal.patient.account.models.enums.PassportGender; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import java.util.Date; @Data @AllArgsConstructor diff --git a/backend/src/main/java/com/personal/patient/account/models/JwtRequest.java b/backend/src/main/java/com/personal/patient/account/models/JwtRequest.java index 8eb469e..6c14e50 100644 --- a/backend/src/main/java/com/personal/patient/account/models/JwtRequest.java +++ b/backend/src/main/java/com/personal/patient/account/models/JwtRequest.java @@ -6,4 +6,4 @@ public class JwtRequest { private String email; private String password; -} +} \ No newline at end of file diff --git a/backend/src/main/java/com/personal/patient/account/service/AuthService.java b/backend/src/main/java/com/personal/patient/account/service/AuthService.java index a8690ef..bb598de 100644 --- a/backend/src/main/java/com/personal/patient/account/service/AuthService.java +++ b/backend/src/main/java/com/personal/patient/account/service/AuthService.java @@ -23,26 +23,30 @@ public class AuthService { private final JwtTokenUtils jwtTokenUtils; private final AuthenticationManager authenticationManager; - public ResponseEntity createAuthToken(JwtRequest authRequest){ - try{ - authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(authRequest.getEmail(), authRequest.getPassword())); + public ResponseEntity createAuthToken(JwtRequest authRequest) { + try { + authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken(authRequest.getEmail(), authRequest.getPassword())); + } catch (BadCredentialsException e) { + return new ResponseEntity<>(new AppError(HttpStatus.UNAUTHORIZED.value(), "Неправильный логин или пароль"), + HttpStatus.UNAUTHORIZED); } - catch (BadCredentialsException e){ - return new ResponseEntity<>(new AppError(HttpStatus.UNAUTHORIZED.value(), "Неправильный логин или пароль"), HttpStatus.UNAUTHORIZED);} UserDetails userDetails = userService.loadUserByUsername(authRequest.getEmail()); String token = jwtTokenUtils.generateToken(userDetails); return ResponseEntity.ok(new JwtResponse(token)); } - public ResponseEntity createNewUser(RegistrationUser registrationUser){ - if(!registrationUser.getPassword().equals(registrationUser.getConfirmPassword())){ - return new ResponseEntity<>(new AppError(HttpStatus.BAD_REQUEST.value(), "Пароли не совпадают"), HttpStatus.BAD_REQUEST); + public ResponseEntity createNewUser(RegistrationUser registrationUser) { + if (!registrationUser.getPassword().equals(registrationUser.getConfirmPassword())) { + return new ResponseEntity<>(new AppError(HttpStatus.BAD_REQUEST.value(), "Пароли не совпадают"), + HttpStatus.BAD_REQUEST); } - if(userService.findByUsername(registrationUser.getEmail()).isPresent()){ - return new ResponseEntity<>(new AppError(HttpStatus.BAD_REQUEST.value(), "Пользовательс указанными именем уже существует"), HttpStatus.BAD_REQUEST); + if (userService.findByUsername(registrationUser.getEmail()).isPresent()) { + return new ResponseEntity<>( + new AppError(HttpStatus.BAD_REQUEST.value(), "Пользователь указанными именем уже существует"), + HttpStatus.BAD_REQUEST); } User user = userService.createNewUser(registrationUser); return ResponseEntity.ok(new UserResponse(user.getId(), user.getEmail())); } } - diff --git a/backend/src/main/java/com/personal/patient/account/service/ProfileService.java b/backend/src/main/java/com/personal/patient/account/service/ProfileService.java index ef1bcb7..aae0ef4 100644 --- a/backend/src/main/java/com/personal/patient/account/service/ProfileService.java +++ b/backend/src/main/java/com/personal/patient/account/service/ProfileService.java @@ -2,6 +2,9 @@ import com.personal.patient.account.entities.User; import com.personal.patient.account.entities.Profile; +import com.personal.patient.account.exceptions.AlreadyExistException; +import com.personal.patient.account.exceptions.NotFoundException; +import com.personal.patient.account.models.FullProfileRepresentation; import com.personal.patient.account.models.ProfileRepresentation; import com.personal.patient.account.models.enums.Gender; import com.personal.patient.account.repositories.ProfileRepository; @@ -10,8 +13,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.security.Principal; import java.util.Date; -import java.util.Optional; @Service @RequiredArgsConstructor @@ -19,13 +22,16 @@ public class ProfileService { private final ProfileRepository profileRepository; - private final UserRepository userRepository; + private final UserService userService; private final DateUtils dateUtils; - public void createOrChangeProfile(ProfileRepresentation profileRequest, User user){ - Optional existProfile = profileRepository.findByUser(user); - Profile profile = existProfile.orElseGet(Profile::new); + public FullProfileRepresentation createProfile(ProfileRepresentation profileRequest, Principal principal){ + User user = userService.getUserByPrincipal(principal); + if(profileRepository.findByUser(user).isPresent()){ + throw new AlreadyExistException("user with email " + user.getEmail() + " alredy have profile"); + } + Profile profile = new Profile(); profile.setUser(user); @@ -40,7 +46,31 @@ public void createOrChangeProfile(ProfileRepresentation profileRequest, User use profile.setAddress(profileRequest.getAddress()); profileRepository.save(profile); user.setProfile(profile); - userRepository.save(user); + userService.save(user); + return new FullProfileRepresentation(profileRequest, user.getId()); + } + + public FullProfileRepresentation changeProfile(ProfileRepresentation profileRequest, Principal principal){ + User user = userService.getUserByPrincipal(principal); + Profile profile = profileRepository.findByUser(user).orElseThrow( + () ->new NotFoundException("user with email " + user.getEmail() + " did not have profile") + ); + + profile.setUser(user); + + profile.setFirstName(profileRequest.getFirstName()); + profile.setMiddleName(profileRequest.getMiddleName()); + profile.setLastName(profileRequest.getLastName()); + + Date date = dateUtils.parseStringToDate(profileRequest.getDateOfBirth()); + profile.setDateOfBirth(date); + profile.setPhoneNumber(profileRequest.getPhoneNumber()); + profile.setGender(Gender.fromValue(profileRequest.getGender())); + profile.setAddress(profileRequest.getAddress()); + profileRepository.save(profile); + user.setProfile(profile); + userService.save(user); + return new FullProfileRepresentation(profileRequest, user.getId()); } public Profile getProfileByUser(User user){ diff --git a/backend/src/main/java/com/personal/patient/account/service/ResultCardService.java b/backend/src/main/java/com/personal/patient/account/service/ResultCardService.java index 5b46e88..6c58cfc 100644 --- a/backend/src/main/java/com/personal/patient/account/service/ResultCardService.java +++ b/backend/src/main/java/com/personal/patient/account/service/ResultCardService.java @@ -32,6 +32,7 @@ public ResultCard createResultCard(CreatingResultCardResponse resultCardResponse resultCard.setDateOfMake(new Date()); Date dateOfShouldReady = dateUtils.parseStringToDate(resultCardResponse.getDateOfShouldReady()); + resultCard.setName(resultCardResponse.getName()); resultCard.setDateOfShouldReady(dateOfShouldReady); resultCard.setDescription(resultCardResponse.getDescription()); resultCard.setHospitalAddress(resultCardResponse.getHospitalAddress()); diff --git a/backend/src/main/java/com/personal/patient/account/service/UserService.java b/backend/src/main/java/com/personal/patient/account/service/UserService.java index efaeb4a..65acf8c 100644 --- a/backend/src/main/java/com/personal/patient/account/service/UserService.java +++ b/backend/src/main/java/com/personal/patient/account/service/UserService.java @@ -61,4 +61,8 @@ public User getUserByPrincipal(Principal principal) { public User getUserByEmail(String email){ return userRepository.findByEmail(email).orElseThrow(() -> new NotFoundException("user with email " + email + " not found")); } + + public User save(User user){ + return userRepository.save(user); + } } diff --git a/backend/src/main/java/com/personal/patient/account/utils/DateUtils.java b/backend/src/main/java/com/personal/patient/account/utils/DateUtils.java index 8f4e5cb..7588a05 100644 --- a/backend/src/main/java/com/personal/patient/account/utils/DateUtils.java +++ b/backend/src/main/java/com/personal/patient/account/utils/DateUtils.java @@ -33,5 +33,9 @@ public Date parseStringToTime(String stringTime){ e.printStackTrace(); } return date; + + public String parseDateToString(Date date){ + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + return dateFormat.format(date); } } diff --git a/backend/src/main/java/com/personal/patient/account/utils/JwtTokenUtils.java b/backend/src/main/java/com/personal/patient/account/utils/JwtTokenUtils.java index b23bc73..7625a43 100644 --- a/backend/src/main/java/com/personal/patient/account/utils/JwtTokenUtils.java +++ b/backend/src/main/java/com/personal/patient/account/utils/JwtTokenUtils.java @@ -5,6 +5,7 @@ import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; diff --git a/client/angular.json b/client/angular.json index d07f75a..2f9035d 100644 --- a/client/angular.json +++ b/client/angular.json @@ -32,12 +32,12 @@ "styles": [ "node_modules/bootstrap/dist/css/bootstrap.min.css", "node_modules/ngx-toastr/toastr.css", + "node_modules/ngx-spinner/animations/ball-scale-multiple.css", "src/styles.scss" ], "scripts": [ "node_modules/bootstrap/dist/js/bootstrap.min.js", "./node_modules/@lottiefiles/lottie-player/dist/lottie-player.js" - ] }, "configurations": { @@ -50,8 +50,8 @@ }, { "type": "anyComponentStyle", - "maximumWarning": "6kb", - "maximumError": "10kb" + "maximumWarning": "60kb", + "maximumError": "100kb" } ], "outputHashing": "all" @@ -179,4 +179,4 @@ "cli": { "analytics": "ee748ae0-81b8-4b6e-b8cd-93d2437bd568" } -} +} \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index a7fbead..ec31bfe 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -13,6 +13,7 @@ "@angular/compiler": "^16.2.12", "@angular/core": "^16.2.12", "@angular/forms": "^16.2.12", + "@angular/material": "^16.2.12", "@angular/platform-browser": "^16.2.12", "@angular/platform-browser-dynamic": "^16.2.12", "@angular/platform-server": "^16.2.12", @@ -21,6 +22,8 @@ "@nguniversal/express-engine": "^16.2.0", "bootstrap": "^5.3.2", "express": "^4.15.2", + "jwt-decode": "^4.0.0", + "moment": "^2.29.4", "ngx-spinner": "^16.0.2", "ngx-toastr": "^17.0.2", "rxjs": "~7.8.0", @@ -278,6 +281,36 @@ "@angular/core": "16.2.12" } }, + "node_modules/@angular/cdk": { + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.2.12.tgz", + "integrity": "sha512-wT8/265zm2WKY0BDaRoYbrAT4kadrmejTRLjuimQIEUKnw4vBsJMWCwQkpFo3s6zr6eznGqYVAFb8KKPVLKGBg==", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^16.0.0 || ^17.0.0", + "@angular/core": "^16.0.0 || ^17.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cdk/node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "optional": true, + "peer": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/@angular/cli": { "version": "16.2.9", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.9.tgz", @@ -480,6 +513,70 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/material": { + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-16.2.12.tgz", + "integrity": "sha512-k1DGRfP1mMmhg/nLJjZBOPzX3SyAjgbRBY2KauKOV8OFCXJGoMn/oLgMBh+qB1WugzIna/31dBV8ruHD3Uvp2w==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/auto-init": "15.0.0-canary.bc9ae6c9c.0", + "@material/banner": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/card": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/chips": "15.0.0-canary.bc9ae6c9c.0", + "@material/circular-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/data-table": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dialog": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/drawer": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/fab": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/form-field": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/image-list": "15.0.0-canary.bc9ae6c9c.0", + "@material/layout-grid": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/linear-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/radio": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/segmented-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/select": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/slider": "15.0.0-canary.bc9ae6c9c.0", + "@material/snackbar": "15.0.0-canary.bc9ae6c9c.0", + "@material/switch": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-bar": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-scroller": "15.0.0-canary.bc9ae6c9c.0", + "@material/textfield": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tooltip": "15.0.0-canary.bc9ae6c9c.0", + "@material/top-app-bar": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^16.0.0 || ^17.0.0", + "@angular/cdk": "16.2.12", + "@angular/common": "^16.0.0 || ^17.0.0", + "@angular/core": "^16.0.0 || ^17.0.0", + "@angular/forms": "^16.0.0 || ^17.0.0", + "@angular/platform-browser": "^16.0.0 || ^17.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/platform-browser": { "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.2.12.tgz", @@ -2952,6 +3049,758 @@ "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" }, + "node_modules/@material/animation": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-leRf+BcZTfC/iSigLXnYgcHAGvFVQveoJT5+2PIRdyPI/bIG7hhciRgacHRsCKC0sGya81dDblLgdkjSUemYLw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/auto-init": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/auto-init/-/auto-init-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-uxzDq7q3c0Bu1pAsMugc1Ik9ftQYQqZY+5e2ybNplT8gTImJhNt4M2mMiMHbMANk2l3UgICmUyRSomgPBWCPIA==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/banner": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/banner/-/banner-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-SHeVoidCUFVhXANN6MNWxK9SZoTSgpIP8GZB7kAl52BywLxtV+FirTtLXkg/8RUkxZRyRWl7HvQ0ZFZa7QQAyA==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/base": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/base/-/base-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Fc3vGuOf+duGo22HTRP6dHdc+MUe0VqQfWOuKrn/wXKD62m0QQR2TqJd3rRhCumH557T5QUyheW943M3E+IGfg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/button": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/button/-/button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-3AQgwrPZCTWHDJvwgKq7Cj+BurQ4wTjDdGL+FEnIGUAjJDskwi1yzx5tW2Wf/NxIi7IoPFyOY3UB41jwMiOrnw==", + "dependencies": { + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/card": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/card/-/card-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-nPlhiWvbLmooTnBmV5gmzB0eLWSgLKsSRBYAbIBmO76Okgz1y+fQNLag+lpm/TDaHVsn5fmQJH8e0zIg0rYsQA==", + "dependencies": { + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/checkbox": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-4tpNnO1L0IppoMF3oeQn8F17t2n0WHB0D7mdJK9rhrujen/fLbekkIC82APB3fdGtLGg3qeNqDqPsJm1YnmrwA==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/chips": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/chips/-/chips-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-fqHKvE5bSWK0bXVkf57MWxZtytGqYBZvvHIOs4JI9HPHEhaJy4CpSw562BEtbm3yFxxALoQknvPW2KYzvADnmA==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/circular-progress": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Lxe8BGAxQwCQqrLhrYrIP0Uok10h7aYS3RBXP41ph+5GmwJd5zdyE2t93qm2dyThvU6qKuXw9726Dtq/N+wvZQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/progress-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/data-table": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/data-table/-/data-table-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-j/7qplT9+sUpfe4pyWhPbl01qJA+OoNAG3VMJruBBR461ZBKyTi7ssKH9yksFGZ8eCEPkOsk/+kDxsiZvRWkeQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/linear-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/select": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/density": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/density/-/density-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Zt3u07fXrBWLW06Tl5fgvjicxNQMkFdawLyNTzZ5TvbXfVkErILLePwwGaw8LNcvzqJP6ABLA8jiR+sKNoJQCg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/dialog": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-o+9a/fmwJ9+gY3Z/uhj/PMVJDq7it1NTWKJn2GwAKdB+fDkT4hb9qEdcxMPyvJJ5ups+XiKZo03+tZrD+38c1w==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/dom": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/dom/-/dom-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-ly78R7aoCJtundSUu0UROU+5pQD5Piae0Y1MkN6bs0724azeazX1KeXFeaf06JOXnlr5/41ol+fSUPowjoqnOg==", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/drawer": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/drawer/-/drawer-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-PFL4cEFnt7VTxDsuspFVNhsFDYyumjU0VWfj3PWB7XudsEfQ3lo85D3HCEtTTbRsCainGN8bgYNDNafLBqiigw==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/elevation": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Ro+Pk8jFuap+T0B0shA3xI1hs2b89dNQ2EIPCNjNMp87emHKAzJfhKb7EZGIwv3+gFLlVaLyIVkb94I89KLsyg==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/fab": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/fab/-/fab-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-dvU0KWMRglwJEQwmQtFAmJcAjzg9VFF6Aqj78bJYu/DAIGFJ1VTTTSgoXM/XCm1YyQEZ7kZRvxBO37CH54rSDg==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/feature-targeting": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-wkDjVcoVEYYaJvun28IXdln/foLgPD7n9ZC9TY76GErGCwTq+HWpU6wBAAk+ePmpRFDayw4vI4wBlaWGxLtysQ==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/floating-label": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-bUWPtXzZITOD/2mkvLkEPO1ngDWmb74y0Kgbz6llHLOQBtycyJIpuoQJ1q2Ez0NM/tFLwPphhAgRqmL3YQ/Kzw==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/focus-ring": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/focus-ring/-/focus-ring-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-cZHThVose3GvAlJzpJoBI1iqL6d1/Jj9hXrR+r8Mwtb1hBIUEG3hxfsRd4vGREuzROPlf0OgNf/V+YHoSwgR5w==", + "dependencies": { + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0" + } + }, + "node_modules/@material/form-field": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-+JFXy5X44Gue1CbZZAQ6YejnI203lebYwL0i6k0ylDpWHEOdD5xkF2PyHR28r9/65Ebcbwbff6q7kI1SGoT7MA==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/icon-button": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-1a0MHgyIwOs4RzxrVljsqSizGYFlM1zY2AZaLDsgT4G3kzsplTx8HZQ022GpUCjAygW+WLvg4z1qAhQHvsbqlw==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/image-list": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/image-list/-/image-list-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-WKWmiYap2iu4QdqmeUSliLlN4O2Ueqa0OuVAYHn/TCzmQ2xmnhZ1pvDLbs6TplpOmlki7vFfe+aSt5SU9gwfOQ==", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/layout-grid": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-5GqmT6oTZhUGWIb+CLD0ZNyDyTiJsr/rm9oRIi3+vCujACwxFkON9tzBlZohdtFS16nuzUusthN6Jt9UrJcN6Q==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/line-ripple": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-8S30WXEuUdgDdBulzUDlPXD6qMzwCX9SxYb5mGDYLwl199cpSGdXHtGgEcCjokvnpLhdZhcT1Dsxeo1g2Evh5Q==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/linear-progress": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-6EJpjrz6aoH2/gXLg9iMe0yF2C42hpQyZoHpmcgTLKeci85ktDvJIjwup8tnk8ULQyFiGiIrhXw2v2RSsiFjvQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/progress-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/list": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/list/-/list-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-TQ1ppqiCMQj/P7bGD4edbIIv4goczZUoiUAaPq/feb1dflvrFMzYqJ7tQRRCyBL8nRhJoI2x99tk8Q2RXvlGUQ==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/menu": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/menu/-/menu-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-IlAh61xzrzxXs38QZlt74UYt8J431zGznSzDtB1Fqs6YFNd11QPKoiRXn1J2Qu/lUxbFV7i8NBKMCKtia0n6/Q==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/menu-surface": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/menu-surface/-/menu-surface-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-dMtSPN+olTWE+08M5qe4ea1IZOhVryYqzK0Gyb2u1G75rSArUxCOB5rr6OC/ST3Mq3RS6zGuYo7srZt4534K9Q==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/notched-outline": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-WuurMg44xexkvLTBTnsO0A+qnzFjpcPdvgWBGstBepYozsvSF9zJGdb1x7Zv1MmqbpYh/Ohnuxtb/Y3jOh6irg==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/progress-indicator": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-uOnsvqw5F2fkeTnTl4MrYzjI7KCLmmLyZaM0cgLNuLsWVlddQE+SGMl28tENx7DUK3HebWq0FxCP8f25LuDD+w==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@material/radio": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/radio/-/radio-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-ehzOK+U1IxQN+OQjgD2lsnf1t7t7RAwQzeO6Czkiuid29ookYbQynWuLWk7NW8H8ohl7lnmfqTP1xSNkkL/F0g==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/ripple": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-JfLW+g3GMVDv4cruQ19+HUxpKVdWCldFlIPw1UYezz2h3WTNDy05S3uP2zUdXzZ01C3dkBFviv4nqZ0GCT16MA==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/rtl": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-SkKLNLFp5QtG7/JEFg9R92qq4MzTcZ5As6sWbH7rRg6ahTHoJEuqE+pOb9Vrtbj84k5gtX+vCYPvCILtSlr2uw==", + "dependencies": { + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/segmented-button": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/segmented-button/-/segmented-button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-YDwkCWP9l5mIZJ7pZJZ2hMDxfBlIGVJ+deNzr8O+Z7/xC5LGXbl4R5aPtUVHygvXAXxpf5096ZD+dSXzYzvWlw==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/select": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/select/-/select-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-unfOWVf7T0sixVG+3k3RTuATfzqvCF6QAzA6J9rlCh/Tq4HuIBNDdV4z19IVu4zwmgWYxY0iSvqWUvdJJYwakQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/shape": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/shape/-/shape-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Dsvr771ZKC46ODzoixLdGwlLEQLfxfLrtnRojXABoZf5G3o9KtJU+J+5Ld5aa960OAsCzzANuaub4iR88b1guA==", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/slider": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/slider/-/slider-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-3AEu+7PwW4DSNLndue47dh2u7ga4hDJRYmuu7wnJCIWJBnLCkp6C92kNc4Rj5iQY2ftJio5aj1gqryluh5tlYg==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/snackbar": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/snackbar/-/snackbar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-TwwQSYxfGK6mc03/rdDamycND6o+1p61WNd7ElZv1F1CLxB4ihRjbCoH7Qo+oVDaP8CTpjeclka+24RLhQq0mA==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/switch": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/switch/-/switch-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-OjUjtT0kRz1ASAsOS+dNzwMwvsjmqy5edK57692qmrP6bL4GblFfBDoiNJ6t0AN4OaKcmL5Hy/xNrTdOZW7Qqw==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab/-/tab-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-s/L9otAwn/pZwVQZBRQJmPqYeNbjoEbzbjMpDQf/VBG/6dJ+aP03ilIBEkqo8NVnCoChqcdtVCoDNRtbU+yp6w==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab-bar": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Xmtq0wJGfu5k+zQeFeNsr4bUKv7L+feCmUp/gsapJ655LQKMXOUQZtSv9ZqWOfrCMy55hoF1CzGFV+oN3tyWWQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-scroller": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab-indicator": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-despCJYi1GrDDq7F2hvLQkObHnSLZPPDxnOzU16zJ6FNYvIdszgfzn2HgAZ6pl5hLOexQ8cla6cAqjTDuaJBhQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tab-scroller": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-QWHG/EWxirj4V9u2IHz+OSY9XCWrnNrPnNgEufxAJVUKV/A8ma1DYeFSQqxhX709R8wKGdycJksg0Flkl7Gq7w==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/textfield": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-R3qRex9kCaZIAK8DuxPnVC42R0OaW7AB7fsFknDKeTeVQvRcbnV8E+iWSdqTiGdsi6QQHifX8idUrXw+O45zPw==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/theme": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-CpUwXGE0dbhxQ45Hu9r9wbJtO/MAlv5ER4tBHA9tp/K+SU+lDgurBE2touFMg5INmdfVNtdumxb0nPPLaNQcUg==", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/tokens": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-nbEuGj05txWz6ZMUanpM47SaAD7soyjKILR+XwDell9Zg3bGhsnexCNXPEz2fD+YgomS+jM5XmIcaJJHg/H93Q==", + "dependencies": { + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0" + } + }, + "node_modules/@material/tooltip": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tooltip/-/tooltip-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-UzuXp0b9NuWuYLYpPguxrjbJnCmT/Cco8CkjI/6JajxaeA3o2XEBbQfRMTq8PTafuBjCHTc0b0mQY7rtxUp1Gg==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/top-app-bar": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/top-app-bar/-/top-app-bar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-vJWjsvqtdSD5+yQ/9vgoBtBSCvPJ5uF/DVssv8Hdhgs1PYaAcODUi77kdi0+sy/TaWyOsTkQixqmwnFS16zesA==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/touch-target": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-AqYh9fjt+tv4ZE0C6MeYHblS2H+XwLbDl2mtyrK0DOEnCVQk5/l5ImKDfhrUdFWHvS4a5nBM4AA+sa7KaroLoA==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "node_modules/@material/typography": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/typography/-/typography-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-CKsG1zyv34AKPNyZC8olER2OdPII64iR2SzQjpqh1UUvmIFiMPk23LvQ1OnC5aCB14pOXzmVgvJt31r9eNdZ6Q==", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, "node_modules/@ngtools/webpack": { "version": "16.2.9", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.9.tgz", @@ -8560,6 +9409,14 @@ "node >= 0.2.0" ] }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, "node_modules/karma": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.2.tgz", @@ -9597,6 +10454,14 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/mrmime": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", @@ -11407,6 +12272,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/safevalues": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/safevalues/-/safevalues-0.3.4.tgz", + "integrity": "sha512-LRneZZRXNgjzwG4bDQdOTSbze3fHm1EAKN/8bePxnlEZiBmkYEDggaHbuvHI9/hoqHbGfsEA7tWS9GhYHZBBsw==" + }, "node_modules/sass": { "version": "1.64.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz", @@ -13637,6 +14507,28 @@ "tslib": "^2.3.0" } }, + "@angular/cdk": { + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.2.12.tgz", + "integrity": "sha512-wT8/265zm2WKY0BDaRoYbrAT4kadrmejTRLjuimQIEUKnw4vBsJMWCwQkpFo3s6zr6eznGqYVAFb8KKPVLKGBg==", + "peer": true, + "requires": { + "parse5": "^7.1.2", + "tslib": "^2.3.0" + }, + "dependencies": { + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "optional": true, + "peer": true, + "requires": { + "entities": "^4.4.0" + } + } + } + }, "@angular/cli": { "version": "16.2.9", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.9.tgz", @@ -13773,6 +14665,61 @@ "tslib": "^2.3.0" } }, + "@angular/material": { + "version": "16.2.12", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-16.2.12.tgz", + "integrity": "sha512-k1DGRfP1mMmhg/nLJjZBOPzX3SyAjgbRBY2KauKOV8OFCXJGoMn/oLgMBh+qB1WugzIna/31dBV8ruHD3Uvp2w==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/auto-init": "15.0.0-canary.bc9ae6c9c.0", + "@material/banner": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/card": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/chips": "15.0.0-canary.bc9ae6c9c.0", + "@material/circular-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/data-table": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dialog": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/drawer": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/fab": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/form-field": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/image-list": "15.0.0-canary.bc9ae6c9c.0", + "@material/layout-grid": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/linear-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/radio": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/segmented-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/select": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/slider": "15.0.0-canary.bc9ae6c9c.0", + "@material/snackbar": "15.0.0-canary.bc9ae6c9c.0", + "@material/switch": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-bar": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-scroller": "15.0.0-canary.bc9ae6c9c.0", + "@material/textfield": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tooltip": "15.0.0-canary.bc9ae6c9c.0", + "@material/top-app-bar": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.3.0" + } + }, "@angular/platform-browser": { "version": "16.2.12", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.2.12.tgz", @@ -15416,6 +16363,758 @@ } } }, + "@material/animation": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-leRf+BcZTfC/iSigLXnYgcHAGvFVQveoJT5+2PIRdyPI/bIG7hhciRgacHRsCKC0sGya81dDblLgdkjSUemYLw==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@material/auto-init": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/auto-init/-/auto-init-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-uxzDq7q3c0Bu1pAsMugc1Ik9ftQYQqZY+5e2ybNplT8gTImJhNt4M2mMiMHbMANk2l3UgICmUyRSomgPBWCPIA==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/banner": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/banner/-/banner-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-SHeVoidCUFVhXANN6MNWxK9SZoTSgpIP8GZB7kAl52BywLxtV+FirTtLXkg/8RUkxZRyRWl7HvQ0ZFZa7QQAyA==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/base": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/base/-/base-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Fc3vGuOf+duGo22HTRP6dHdc+MUe0VqQfWOuKrn/wXKD62m0QQR2TqJd3rRhCumH557T5QUyheW943M3E+IGfg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@material/button": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/button/-/button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-3AQgwrPZCTWHDJvwgKq7Cj+BurQ4wTjDdGL+FEnIGUAjJDskwi1yzx5tW2Wf/NxIi7IoPFyOY3UB41jwMiOrnw==", + "requires": { + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/card": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/card/-/card-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-nPlhiWvbLmooTnBmV5gmzB0eLWSgLKsSRBYAbIBmO76Okgz1y+fQNLag+lpm/TDaHVsn5fmQJH8e0zIg0rYsQA==", + "requires": { + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/checkbox": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-4tpNnO1L0IppoMF3oeQn8F17t2n0WHB0D7mdJK9rhrujen/fLbekkIC82APB3fdGtLGg3qeNqDqPsJm1YnmrwA==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/chips": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/chips/-/chips-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-fqHKvE5bSWK0bXVkf57MWxZtytGqYBZvvHIOs4JI9HPHEhaJy4CpSw562BEtbm3yFxxALoQknvPW2KYzvADnmA==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "@material/circular-progress": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Lxe8BGAxQwCQqrLhrYrIP0Uok10h7aYS3RBXP41ph+5GmwJd5zdyE2t93qm2dyThvU6qKuXw9726Dtq/N+wvZQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/progress-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/data-table": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/data-table/-/data-table-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-j/7qplT9+sUpfe4pyWhPbl01qJA+OoNAG3VMJruBBR461ZBKyTi7ssKH9yksFGZ8eCEPkOsk/+kDxsiZvRWkeQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/linear-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/select": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/density": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/density/-/density-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Zt3u07fXrBWLW06Tl5fgvjicxNQMkFdawLyNTzZ5TvbXfVkErILLePwwGaw8LNcvzqJP6ABLA8jiR+sKNoJQCg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@material/dialog": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-o+9a/fmwJ9+gY3Z/uhj/PMVJDq7it1NTWKJn2GwAKdB+fDkT4hb9qEdcxMPyvJJ5ups+XiKZo03+tZrD+38c1w==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/dom": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/dom/-/dom-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-ly78R7aoCJtundSUu0UROU+5pQD5Piae0Y1MkN6bs0724azeazX1KeXFeaf06JOXnlr5/41ol+fSUPowjoqnOg==", + "requires": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/drawer": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/drawer/-/drawer-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-PFL4cEFnt7VTxDsuspFVNhsFDYyumjU0VWfj3PWB7XudsEfQ3lo85D3HCEtTTbRsCainGN8bgYNDNafLBqiigw==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/elevation": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Ro+Pk8jFuap+T0B0shA3xI1hs2b89dNQ2EIPCNjNMp87emHKAzJfhKb7EZGIwv3+gFLlVaLyIVkb94I89KLsyg==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/fab": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/fab/-/fab-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-dvU0KWMRglwJEQwmQtFAmJcAjzg9VFF6Aqj78bJYu/DAIGFJ1VTTTSgoXM/XCm1YyQEZ7kZRvxBO37CH54rSDg==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/feature-targeting": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-wkDjVcoVEYYaJvun28IXdln/foLgPD7n9ZC9TY76GErGCwTq+HWpU6wBAAk+ePmpRFDayw4vI4wBlaWGxLtysQ==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@material/floating-label": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-bUWPtXzZITOD/2mkvLkEPO1ngDWmb74y0Kgbz6llHLOQBtycyJIpuoQJ1q2Ez0NM/tFLwPphhAgRqmL3YQ/Kzw==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/focus-ring": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/focus-ring/-/focus-ring-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-cZHThVose3GvAlJzpJoBI1iqL6d1/Jj9hXrR+r8Mwtb1hBIUEG3hxfsRd4vGREuzROPlf0OgNf/V+YHoSwgR5w==", + "requires": { + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0" + } + }, + "@material/form-field": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-+JFXy5X44Gue1CbZZAQ6YejnI203lebYwL0i6k0ylDpWHEOdD5xkF2PyHR28r9/65Ebcbwbff6q7kI1SGoT7MA==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/icon-button": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-1a0MHgyIwOs4RzxrVljsqSizGYFlM1zY2AZaLDsgT4G3kzsplTx8HZQ022GpUCjAygW+WLvg4z1qAhQHvsbqlw==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/image-list": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/image-list/-/image-list-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-WKWmiYap2iu4QdqmeUSliLlN4O2Ueqa0OuVAYHn/TCzmQ2xmnhZ1pvDLbs6TplpOmlki7vFfe+aSt5SU9gwfOQ==", + "requires": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/layout-grid": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-5GqmT6oTZhUGWIb+CLD0ZNyDyTiJsr/rm9oRIi3+vCujACwxFkON9tzBlZohdtFS16nuzUusthN6Jt9UrJcN6Q==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@material/line-ripple": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-8S30WXEuUdgDdBulzUDlPXD6qMzwCX9SxYb5mGDYLwl199cpSGdXHtGgEcCjokvnpLhdZhcT1Dsxeo1g2Evh5Q==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/linear-progress": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-6EJpjrz6aoH2/gXLg9iMe0yF2C42hpQyZoHpmcgTLKeci85ktDvJIjwup8tnk8ULQyFiGiIrhXw2v2RSsiFjvQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/progress-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/list": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/list/-/list-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-TQ1ppqiCMQj/P7bGD4edbIIv4goczZUoiUAaPq/feb1dflvrFMzYqJ7tQRRCyBL8nRhJoI2x99tk8Q2RXvlGUQ==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/menu": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/menu/-/menu-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-IlAh61xzrzxXs38QZlt74UYt8J431zGznSzDtB1Fqs6YFNd11QPKoiRXn1J2Qu/lUxbFV7i8NBKMCKtia0n6/Q==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/menu-surface": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/menu-surface/-/menu-surface-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-dMtSPN+olTWE+08M5qe4ea1IZOhVryYqzK0Gyb2u1G75rSArUxCOB5rr6OC/ST3Mq3RS6zGuYo7srZt4534K9Q==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/notched-outline": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-WuurMg44xexkvLTBTnsO0A+qnzFjpcPdvgWBGstBepYozsvSF9zJGdb1x7Zv1MmqbpYh/Ohnuxtb/Y3jOh6irg==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/progress-indicator": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-uOnsvqw5F2fkeTnTl4MrYzjI7KCLmmLyZaM0cgLNuLsWVlddQE+SGMl28tENx7DUK3HebWq0FxCP8f25LuDD+w==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@material/radio": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/radio/-/radio-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-ehzOK+U1IxQN+OQjgD2lsnf1t7t7RAwQzeO6Czkiuid29ookYbQynWuLWk7NW8H8ohl7lnmfqTP1xSNkkL/F0g==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/ripple": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-JfLW+g3GMVDv4cruQ19+HUxpKVdWCldFlIPw1UYezz2h3WTNDy05S3uP2zUdXzZ01C3dkBFviv4nqZ0GCT16MA==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/rtl": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-SkKLNLFp5QtG7/JEFg9R92qq4MzTcZ5As6sWbH7rRg6ahTHoJEuqE+pOb9Vrtbj84k5gtX+vCYPvCILtSlr2uw==", + "requires": { + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/segmented-button": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/segmented-button/-/segmented-button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-YDwkCWP9l5mIZJ7pZJZ2hMDxfBlIGVJ+deNzr8O+Z7/xC5LGXbl4R5aPtUVHygvXAXxpf5096ZD+dSXzYzvWlw==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/select": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/select/-/select-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-unfOWVf7T0sixVG+3k3RTuATfzqvCF6QAzA6J9rlCh/Tq4HuIBNDdV4z19IVu4zwmgWYxY0iSvqWUvdJJYwakQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/shape": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/shape/-/shape-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Dsvr771ZKC46ODzoixLdGwlLEQLfxfLrtnRojXABoZf5G3o9KtJU+J+5Ld5aa960OAsCzzANuaub4iR88b1guA==", + "requires": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/slider": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/slider/-/slider-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-3AEu+7PwW4DSNLndue47dh2u7ga4hDJRYmuu7wnJCIWJBnLCkp6C92kNc4Rj5iQY2ftJio5aj1gqryluh5tlYg==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/snackbar": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/snackbar/-/snackbar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-TwwQSYxfGK6mc03/rdDamycND6o+1p61WNd7ElZv1F1CLxB4ihRjbCoH7Qo+oVDaP8CTpjeclka+24RLhQq0mA==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/switch": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/switch/-/switch-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-OjUjtT0kRz1ASAsOS+dNzwMwvsjmqy5edK57692qmrP6bL4GblFfBDoiNJ6t0AN4OaKcmL5Hy/xNrTdOZW7Qqw==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "@material/tab": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab/-/tab-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-s/L9otAwn/pZwVQZBRQJmPqYeNbjoEbzbjMpDQf/VBG/6dJ+aP03ilIBEkqo8NVnCoChqcdtVCoDNRtbU+yp6w==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/tab-bar": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Xmtq0wJGfu5k+zQeFeNsr4bUKv7L+feCmUp/gsapJ655LQKMXOUQZtSv9ZqWOfrCMy55hoF1CzGFV+oN3tyWWQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-scroller": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/tab-indicator": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-despCJYi1GrDDq7F2hvLQkObHnSLZPPDxnOzU16zJ6FNYvIdszgfzn2HgAZ6pl5hLOexQ8cla6cAqjTDuaJBhQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/tab-scroller": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-QWHG/EWxirj4V9u2IHz+OSY9XCWrnNrPnNgEufxAJVUKV/A8ma1DYeFSQqxhX709R8wKGdycJksg0Flkl7Gq7w==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/textfield": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-R3qRex9kCaZIAK8DuxPnVC42R0OaW7AB7fsFknDKeTeVQvRcbnV8E+iWSdqTiGdsi6QQHifX8idUrXw+O45zPw==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/theme": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-CpUwXGE0dbhxQ45Hu9r9wbJtO/MAlv5ER4tBHA9tp/K+SU+lDgurBE2touFMg5INmdfVNtdumxb0nPPLaNQcUg==", + "requires": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/tokens": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-nbEuGj05txWz6ZMUanpM47SaAD7soyjKILR+XwDell9Zg3bGhsnexCNXPEz2fD+YgomS+jM5XmIcaJJHg/H93Q==", + "requires": { + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0" + } + }, + "@material/tooltip": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tooltip/-/tooltip-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-UzuXp0b9NuWuYLYpPguxrjbJnCmT/Cco8CkjI/6JajxaeA3o2XEBbQfRMTq8PTafuBjCHTc0b0mQY7rtxUp1Gg==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "safevalues": "^0.3.4", + "tslib": "^2.1.0" + } + }, + "@material/top-app-bar": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/top-app-bar/-/top-app-bar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-vJWjsvqtdSD5+yQ/9vgoBtBSCvPJ5uF/DVssv8Hdhgs1PYaAcODUi77kdi0+sy/TaWyOsTkQixqmwnFS16zesA==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/touch-target": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-AqYh9fjt+tv4ZE0C6MeYHblS2H+XwLbDl2mtyrK0DOEnCVQk5/l5ImKDfhrUdFWHvS4a5nBM4AA+sa7KaroLoA==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, + "@material/typography": { + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/typography/-/typography-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-CKsG1zyv34AKPNyZC8olER2OdPII64iR2SzQjpqh1UUvmIFiMPk23LvQ1OnC5aCB14pOXzmVgvJt31r9eNdZ6Q==", + "requires": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.1.0" + } + }, "@ngtools/webpack": { "version": "16.2.9", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-16.2.9.tgz", @@ -19674,6 +21373,11 @@ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true }, + "jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==" + }, "karma": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.2.tgz", @@ -20483,6 +22187,11 @@ "minimist": "^1.2.6" } }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==" + }, "mrmime": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", @@ -21795,6 +23504,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "safevalues": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/safevalues/-/safevalues-0.3.4.tgz", + "integrity": "sha512-LRneZZRXNgjzwG4bDQdOTSbze3fHm1EAKN/8bePxnlEZiBmkYEDggaHbuvHI9/hoqHbGfsEA7tWS9GhYHZBBsw==" + }, "sass": { "version": "1.64.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.64.1.tgz", diff --git a/client/package.json b/client/package.json index 3e6d80e..d7e00aa 100644 --- a/client/package.json +++ b/client/package.json @@ -19,6 +19,7 @@ "@angular/compiler": "^16.2.12", "@angular/core": "^16.2.12", "@angular/forms": "^16.2.12", + "@angular/material": "^16.2.12", "@angular/platform-browser": "^16.2.12", "@angular/platform-browser-dynamic": "^16.2.12", "@angular/platform-server": "^16.2.12", @@ -27,6 +28,8 @@ "@nguniversal/express-engine": "^16.2.0", "bootstrap": "^5.3.2", "express": "^4.15.2", + "jwt-decode": "^4.0.0", + "moment": "^2.29.4", "ngx-spinner": "^16.0.2", "ngx-toastr": "^17.0.2", "rxjs": "~7.8.0", diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 82dbd99..141553a 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -3,23 +3,46 @@ import { RouterModule, Routes } from '@angular/router'; import { HomeComponent } from './modules/home/home.component'; import { LoginRegisterComponent } from './components/login-register/login-register.component'; import { MedicalCardComponent } from './components/medical-card/medical-card.component'; +import { AppointmentScheduler } from './components/appointment-scheduler/appointment-scheduler.component'; +import { DoctorsList } from './components/doctors-list/doctors-list.component'; import { ChatComponent } from './components/chat/chat.component'; import { AuthGuard } from './core/guards/auth.guard'; +import { AppointmentsComponent } from './components/appointments/appointments.component'; const routes: Routes = [ { path: '', redirectTo: '/login-reg', pathMatch: 'full' }, { path: 'login-reg', component: LoginRegisterComponent }, { path: 'profile', component: HomeComponent, canActivate: [AuthGuard] }, - { path: 'electronic-medical-card', component: MedicalCardComponent, canActivate: [AuthGuard] }, + { + path: 'schedule-an-appointment', + component: AppointmentScheduler, + canActivate: [AuthGuard], + }, + { + path: 'list-of-doctors', + component: DoctorsList, + canActivate: [AuthGuard], + }, + { + path: 'electronic-medical-card', + component: MedicalCardComponent, + canActivate: [AuthGuard], + }, + { + path: 'appointments', + component: AppointmentsComponent, + canActivate: [AuthGuard], + }, { path: 'chat', component: ChatComponent, canActivate: [AuthGuard] }, // ... other routes ... ]; - @NgModule({ - imports: [RouterModule.forRoot(routes, { - initialNavigation: 'enabledBlocking' -})], + imports: [ + RouterModule.forRoot(routes, { + initialNavigation: 'enabledBlocking', + }), + ], exports: [RouterModule], }) export class AppRoutingModule {} diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 77fd0cd..3eb3d27 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -9,6 +9,7 @@ import { HomeComponent } from './modules/home/home.component'; import { LoginRegisterComponent } from './components/login-register/login-register.component'; import { MedicalCardComponent } from './components/medical-card/medical-card.component'; import { ChatComponent } from './components/chat/chat.component'; +import { AppointmentScheduler } from './components/appointment-scheduler/appointment-scheduler.component'; import { ReactiveFormsModule } from '@angular/forms'; import { AuthInterceptor } from './core/interceptors/auth.interceptor'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; @@ -17,6 +18,7 @@ import { ToastrModule } from 'ngx-toastr'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NgxSpinnerModule } from 'ngx-spinner'; import { LoadingInterceptor } from './core/interceptors/loading.interceptor'; +import { MatTabsModule } from '@angular/material/tabs'; @NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA], @@ -35,6 +37,8 @@ import { LoadingInterceptor } from './core/interceptors/loading.interceptor'; SharedModule, HttpClientModule, BrowserAnimationsModule, + AppointmentScheduler, + MatTabsModule, ToastrModule.forRoot({ positionClass: 'toast-top-center', preventDuplicates: true, diff --git a/client/src/app/components/appointment-scheduler/appointment-scheduler.component.html b/client/src/app/components/appointment-scheduler/appointment-scheduler.component.html new file mode 100644 index 0000000..33c5496 --- /dev/null +++ b/client/src/app/components/appointment-scheduler/appointment-scheduler.component.html @@ -0,0 +1,154 @@ + +
+
+ +
+ +
Добрый день, {{ profileData?.firstName }}
+ +
+
Выберите удобную дату посещения
+ + Выбрать дату + + ДД/ММ/ГГГГ + + + + + + + + +
+ +
+
+
Выберите нужного специалиста
+ +
+
+
Выберите врача
+ +
+
+ + + +
+
Расписание приема {{form.doctor}}
+ +
Осталось свободных слотов: {{form.ticketsLeft}}
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Пн. + + {{element.mnd}} + Вт. {{element.tsd}} + + Ср. {{element.wnd}} + + Чт. {{element.trd}} + + Пт. {{element.frd}} + + Сб. + {{element.std}} + + Вс. {{element.snd}} +
+
+ + +
+ +
+ +
+ +
+ +
+ + + + + +
+
+ + + + + +
\ No newline at end of file diff --git a/client/src/app/components/appointment-scheduler/appointment-scheduler.component.scss b/client/src/app/components/appointment-scheduler/appointment-scheduler.component.scss new file mode 100644 index 0000000..59e4443 --- /dev/null +++ b/client/src/app/components/appointment-scheduler/appointment-scheduler.component.scss @@ -0,0 +1,179 @@ +@import "../../../styles.scss"; + +.container { + background-color: transparent; + margin-top: 1rem; + margin-bottom: 2rem; + border-radius: 1rem; + box-shadow: 0 4px 8px rgba(62, 96, 207, 0.1), + 0 0 10px rgba(131, 176, 235, 0.1); // Enhanced shadow + border: 1px solid $dark-button-bg; + padding: 20px; + border-radius: 10px; + + + + @media (max-width: 768px) { // Responsive design for mobile and tablet + margin: 0.5rem; + padding: 10px; + } +} + +i{ + color: #007bff; + margin-right: 10px; +} + + + +.day-selected { + background-color: greenyellow; +} + +.greeting { + display: flex; + justify-content: flex-start; + padding: 1rem; + font-size: large; + +} + + +.appointment-actions { + display: flex; + justify-content: space-between; // Changed to space-between for better spacing + margin-top: 20px; + flex-wrap: wrap; // Allows buttons to wrap on smaller screens + + @media (min-width: 768px) { // For tablets and desktops + justify-content: space-between; // Buttons at the edges of the card + } + } + + .btn-appointment { + padding: 10px 20px; + border-radius: 5px; + background-color: $light-button-bg; + color: $light-button-text; + border: none; + cursor: pointer; + transition: background-color 0.3s ease; + flex-grow: 1; // Allows buttons to grow and fill the space + + &:not(:last-child) { + margin-bottom: 10px; // Adds space between buttons on mobile + } + + &:hover { + background-color: darken($light-button-bg, 10%); + } + + @media (min-width: 768px) { // For tablets and desktops + flex-grow: 0; // Prevents buttons from growing + &:not(:last-child) { + margin-bottom: 0; // Removes the bottom margin on larger screens + margin-right: 10px; // Adds space between buttons + } + } + } + +.dateToVisitContainer, .speciality, .doctor { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem; + font-size: medium; + + .date-label, div { + display: flex; + align-items: center; + i { + margin-right: 10px; + } + } + + @media (max-width: 768px) { + flex-direction: column; + align-items: flex-start; + } +} + +.datepickerButton { + border: 1px solid lemonchiffon; + border-radius: 1rem; +} + +.selectors { +display: flex; +flex-direction: column; +} + + +.selectedDoctor { + color: goldenrod; +} + +.doctorAvailabilityContainer { + display: flex; + flex-direction: column; + row-gap: 0.5rem; + padding: 1rem; + + @media (max-width: 768px) { + // Adjustments for smaller screens + } +} + +.ticketsLeft { + color: rebeccapurple; +} + +.table-container { + overflow-x: auto; // Allows table to scroll horizontally on smaller screens +} + +.mat-table { + width: 100%; + min-width: 600px; // Ensures the table is still legible on smaller screens + + th, td { + text-align: center; + padding: 0.5rem; + } + + .day-selected { + background-color: greenyellow; // Highlight selected day + } +} + + +.table-scroll-container { + overflow-x: auto; // Enable horizontal scrolling for the table + -webkit-overflow-scrolling: touch; // Smooth scrolling on touch devices + margin-bottom: 1rem; +} + +.buttonContainer { + padding: 1rem; + display: flex; + justify-content: center; // Center the button +} + +.submitButton { + padding: 10px; + cursor: pointer; + background-color: rgba(0, 123, 255, 0.1); + transition: background-color 0.3s ease; + border-radius: 5px; + color: #007bff; + border: none; + width: 100%; // Make button full width for better mobile responsiveness + + @media (max-width: 768px) { + width: auto; // Adjust width on smaller screens + } +} +.successButton { + color: green; + background-color: transparent; +} diff --git a/client/src/app/components/appointment-scheduler/appointment-scheduler.component.ts b/client/src/app/components/appointment-scheduler/appointment-scheduler.component.ts new file mode 100644 index 0000000..0a10bac --- /dev/null +++ b/client/src/app/components/appointment-scheduler/appointment-scheduler.component.ts @@ -0,0 +1,256 @@ +import { Component } from '@angular/core'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { MatInputModule } from '@angular/material/input'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatNativeDateModule } from '@angular/material/core'; +import { MatTableModule } from '@angular/material/table'; +import { CommonModule } from '@angular/common'; +import { MatSelectModule } from '@angular/material/select'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { Router } from '@angular/router'; +import { ProfileData } from 'src/app/core/models/profile-data.model'; +import { ProfileService } from 'src/app/core/services/profile.service'; + +function getJsonFromUrl(url: string) { + if (!url) url = location.search; + var result: any = {}; + url.split('&').forEach(function (part) { + var item = part.split('='); + result[item[0]] = decodeURIComponent(item[1]); + }); + return result; +} +interface DoctorTimetable { + mnd: string; + tsd: string; + wnd: string; + trd: string; + frd: string; + std: string; + snd: string; +} + +const DOCTORS_TIMETABLE: DoctorTimetable[] = [ + { + mnd: '11:00 - 15-00', + tsd: '9:00 - 19-00', + wnd: '9:00 - 19-00', + trd: '9:00 - 19-00', + frd: '9:00 - 19-00', + std: '9:00 - 12-00', + snd: 'Выходной', + }, + { + mnd: '09:00 - 15-00', + tsd: '14:00 - 19-00', + wnd: 'Выходной', + trd: '11:00 - 16-00', + frd: '11:00 - 16-00', + std: '11:00 - 19-00', + snd: '10:00 - 14-00', + }, + { + mnd: '09:00 - 15-00', + tsd: '11:00 - 16-00', + wnd: 'Выходной', + trd: '11:00 - 16-00', + frd: '09:00 - 15-00', + std: '11:00 - 19-00', + snd: '10:00 - 14-00', + }, + { + mnd: 'Выходной', + tsd: '14:00 - 19-00', + wnd: 'Выходной', + trd: '10:00 - 14-00', + frd: '9:00 - 19-00', + std: '14:00 - 19-00', + snd: '11:00 - 16-00', + }, +]; + +const DOCTORS = [ + 'Петров А.И.', + 'Сидоров Т.К.', + 'Андреев Ж.К.', + 'Васильев А.В.', + 'Терентьев А.Т.', + 'Богомолов Е. Е.', + 'Ольгина А. Г.', + 'Ефремов Е. А.', + 'Умнова А. А.', + 'Карагонова В. П.', +]; + +@Component({ + selector: 'app-appointment-scheduler', + templateUrl: './appointment-scheduler.component.html', + imports: [ + MatFormFieldModule, + MatInputModule, + MatDatepickerModule, + MatNativeDateModule, + MatTableModule, + CommonModule, + MatFormFieldModule, + MatSelectModule, + FormsModule, + ReactiveFormsModule, + MatInputModule, + ], + standalone: true, + styleUrls: ['./appointment-scheduler.component.scss'], +}) +export class AppointmentScheduler { + profileData!: ProfileData; + specialities: string[] = [ + 'АКУШЕР-ГИНЕКОЛОГ', + 'ВОП', + 'Вакцинация', + 'ДИСПАНСЕРИЗАЦИЯ -> ГИНЕКОЛОГИ', + 'Диспансеризация/Углуб. диспансеризация', + 'ИНФЕКЦИОНИСТ', + 'ОСТЕОПОРОЗ', + 'ТЕРАПЕВТ', + 'УРОЛОГ', + 'Симптомы ОРВИ/Грипп/COVID', + 'КАРДИОЛОГ', + 'ОТОЛАРИНГОЛОГ', + 'ОФТАЛЬМОЛОГ', + 'ПСИХОЛОГ', + 'ХИРУРГ', + 'ЭНДОКРИНОЛОГ', + ]; + + user = { + firstName: 'Иван', + lastName: 'Иванов', + }; + minDate = new Date(); + + displayedColumns: string[] = [ + 'mnd', + 'tsd', + 'wnd', + 'trd', + 'frd', + 'std', + 'snd', + ]; + + doctors = DOCTORS; + + form = { + speciality: 'empty', + doctor: 'empty', + date: 'empty', + ticketsLeft: Math.trunc(Math.random() * 100), + isAllFieldsFilled: false, + timetable: { + isSelectedDay: 'empty', + }, + dataSource: [DOCTORS_TIMETABLE[Math.round(Math.random() * 3)]], + buttonToggle: false, + }; + + constructor(private router: Router, private profileService: ProfileService,) {} + + ngOnInit() { + const paramsParts = this.router.url.split(';'); + this.loadProfile(); + + const newDoctor: { doctorName: string } | undefined = getJsonFromUrl( + paramsParts[1] + ); + + const newSpeciality: { speciality: string } | undefined = getJsonFromUrl( + paramsParts[2] + ); + + if (newDoctor?.doctorName && newSpeciality?.speciality) { + this.form.doctor = newDoctor?.doctorName; + this.form.speciality = newSpeciality?.speciality; + } + } + + loadProfile(): void { + this.profileService.getProfile().subscribe({ + next: (profile) => { + if (profile) { + //this.profileForm.patchValue(profile); + this.profileData = profile; + } else { + //this.toastr.info("Пожалуйста, заполните информацию в вашем профиле."); + // Redirect to profile completion page if needed + } + }, + error: (error) => { + // Handle errors if needed + } + }); + } + + onReturnToAppointments() { + this.router.navigate(['/appointments']); + } + + updateSpeciality(e: Event) { + this.form.speciality = (e.target as HTMLInputElement).value; + this.form.isAllFieldsFilled = + !!this.form.date && + this.form.speciality !== 'empty' && + this.form.doctor !== 'empty' && + this.form.date !== 'empty' && + this.form.timetable.isSelectedDay !== 'empty'; + } + + updateDoctor(e: Event) { + this.form.doctor = (e.target as HTMLInputElement).value; + this.form.ticketsLeft = Math.trunc(Math.random() * 100); + this.form.isAllFieldsFilled = + !!this.form.date && + this.form.speciality !== 'empty' && + this.form.doctor !== 'empty' && + this.form.date !== 'empty' && + this.form.timetable.isSelectedDay !== 'empty'; + this.form.dataSource = [DOCTORS_TIMETABLE[Math.round(Math.random() * 3)]]; + } + + updateDate(e: any) { + this.form.date = e.target.value; + this.form.isAllFieldsFilled = + !!this.form.date && + this.form.speciality !== 'empty' && + this.form.doctor !== 'empty' && + this.form.date !== 'empty' && + this.form.timetable.isSelectedDay !== 'empty'; + } + + onClick(dayOfWeek: string) { + this.form.timetable.isSelectedDay = dayOfWeek; + this.form.isAllFieldsFilled = + !!this.form.date && + this.form.speciality !== 'empty' && + this.form.doctor !== 'empty' && + this.form.date !== 'empty' && + this.form.timetable.isSelectedDay !== 'empty'; + } + + onSubmit() { + const { + form: { + doctor, + speciality, + date, + timetable: { isSelectedDay }, + }, + } = this; + + this.form.buttonToggle = !this.form.buttonToggle; + + setTimeout(() => { + this.form.buttonToggle = !this.form.buttonToggle; + }, 1000); + // TODO: send the request to create new visit. + } +} diff --git a/client/src/app/components/appointments/appointments.component.html b/client/src/app/components/appointments/appointments.component.html new file mode 100644 index 0000000..58d9bb1 --- /dev/null +++ b/client/src/app/components/appointments/appointments.component.html @@ -0,0 +1,182 @@ + + + +
+
+ + +
+
+ + +
+ +
+

Приемы

+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ВрачДатаВремя
Доктор Иванов01.01.202310:00
Доктор Петров05.01.202314:00
Доктор Сидоров10.01.202316:00
Доктор Иванов15.01.202310:00
Доктор Петров05.01.202314:00
Доктор Сидоров10.01.202316:00
Доктор Иванов15.01.202310:00
Доктор Петров05.01.202314:00
Доктор Сидоров10.01.202316:00
Доктор Иванов15.01.202310:00
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Врач Дата Время
Доктор Петров10.01.202314:00
Доктор Сидоров15.01.202316:00
Доктор Иванов20.01.202310:00
Доктор Петров25.01.202314:00
Доктор Сидоров30.01.202316:00
Доктор Иванов20.01.202310:00
Доктор Петров25.01.202314:00
Доктор Сидоров30.01.202316:00
+
+
+
+ +
+ + + + + +
+
+ + + + + +
\ No newline at end of file diff --git a/client/src/app/components/appointments/appointments.component.scss b/client/src/app/components/appointments/appointments.component.scss new file mode 100644 index 0000000..c6079f5 --- /dev/null +++ b/client/src/app/components/appointments/appointments.component.scss @@ -0,0 +1,185 @@ +@import "../../../styles.scss"; + +.appointment-card-container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +.appointment-card-banner { + box-shadow: 0 4px 8px rgba(62, 96, 207, 0.1), + 0 0 10px rgba(131, 176, 235, 0.1); // Enhanced shadow + border: 1px solid $dark-button-bg; + padding: 20px; + border-radius: 10px; + margin-bottom: 20px; + display: flex; + align-items: center; + justify-content: space-between; +} + +.banner-content { + flex: 1; + text-align: center; +} + +.banner-content i { + margin-bottom: 10px; // Space between the icon and the heading + color: #007bff; // Color of the icon +} + +.banner-image { + flex-basis: 30%; // Adjust as needed + padding-left: 20px; // Space between text and image +} + +.banner-image img { + max-width: 100%; + border-radius: 10px; // Optional: to make the image corners rounded +} + + + +.table-card { + border: 1px solid #007bff; + //background-color: rgba(255, 255, 255, 0.1); // Transparent background + border-radius: 10px; + padding: 20px; + margin-bottom: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + } + + .table-scroll { + max-height: 400px; // Set a maximum height + overflow-y: auto; // Enable vertical scrolling + margin-bottom: 20px; // Optional, for spacing + + table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + margin-top: 10px; + } + + // Rest of your table styles... +} + + + .appointments-table + { + margin-top: 20px; + } + + table { + width: 100%; + border-collapse: separate; // Change to separate + border-spacing: 0; // Add this to ensure there's no spacing between cells + margin-top: 10px; + } + + table th, + table td { + padding: 12px 10px; // Increased padding for better spacing + text-align: left; + position: relative; // To position the pseudo-element + } + + table tr:not(:last-child) th, + table tr:not(:last-child) td { + border-bottom: 1px solid rgba(224, 224, 224, 0.5); // Lighter and semi-transparent border + } + + table th { + background-color: rgba( + 0, + 123, + 255, + 0.1 + ); // A very light blue background for headers + border-radius: 10px; // Rounded corners for the top + } + + .tabs { + display: flex; + margin-bottom: 10px; + border-radius: 10px; // Added corner radius for the container + overflow: hidden; // Ensures inner buttons conform to the container's border-radius + } + + .tabs button { + flex: 1; + padding: 10px; + cursor: pointer; + border: none; + background-color: rgba( + 0, + 123, + 255, + 0.1 + ); // Light blue background for better visibility + transition: background-color 0.3s ease; + border-radius: 5px; // Corner radius for individual buttons + color: #007bff; // Blue text color for better visibility + } + + .tabs button.active { + background-color: $dark-button-bg; + color: $dark-text; // Darker text color for active tab + } + + + .appointment-actions { + display: flex; + justify-content: space-between; // Changed to space-between for better spacing + margin-top: 20px; + flex-wrap: wrap; // Allows buttons to wrap on smaller screens + + @media (min-width: 768px) { // For tablets and desktops + justify-content: space-between; // Buttons at the edges of the card + } + } + + .btn-appointment { + padding: 10px 20px; + border-radius: 5px; + background-color: $light-button-bg; + color: $light-button-text; + border: none; + cursor: pointer; + transition: background-color 0.3s ease; + flex-grow: 1; // Allows buttons to grow and fill the space + + &:not(:last-child) { + margin-bottom: 10px; // Adds space between buttons on mobile + } + + &:hover { + background-color: darken($light-button-bg, 10%); + } + + @media (min-width: 768px) { // For tablets and desktops + flex-grow: 0; // Prevents buttons from growing + &:not(:last-child) { + margin-bottom: 0; // Removes the bottom margin on larger screens + margin-right: 10px; // Adds space between buttons + } + } + } + + + /* Responsive Design */ +@media (max-width: 768px) { + .appointment-card-banner { + flex-direction: column; + } + + .banner-content { + margin-bottom: 20px; + } + + .banner-image { + flex-basis: 100%; + padding-left: 0; + } + } + \ No newline at end of file diff --git a/client/src/app/components/appointments/appointments.component.ts b/client/src/app/components/appointments/appointments.component.ts new file mode 100644 index 0000000..54e9cc8 --- /dev/null +++ b/client/src/app/components/appointments/appointments.component.ts @@ -0,0 +1,20 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-appointments', + templateUrl: './appointments.component.html', + styleUrls: ['./appointments.component.scss'], +}) +export class AppointmentsComponent { + activeTab: 'upcoming' | 'past' = 'upcoming'; + + constructor(private router: Router) {} + bookNewAppointment() { + this.router.navigate(['/schedule-an-appointment']); + } + + viewDoctors() { + this.router.navigate(['/list-of-doctors']); + } +} diff --git a/client/src/app/components/doctors-list/doctors-list.component.html b/client/src/app/components/doctors-list/doctors-list.component.html new file mode 100644 index 0000000..31bb46c --- /dev/null +++ b/client/src/app/components/doctors-list/doctors-list.component.html @@ -0,0 +1,132 @@ + + + +
+
+ + +
+
+ +
+ +
+

Доктора

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ВрачСпециальностьДни приема
Петров А.И.АКУШЕР-ГИНЕКОЛОГПн-Сб: 10:00-18:00
+ Вс: выходной +
Сидоров Т.К.ВакцинацияПн-Чт: 10:00-18:00
+ Пт-Сб: 12:00-16:00
+ Вс: выходной
Андреев Ж.К.ТЕРАПЕВТПн-Сб: 12:00-15:00
+ Вс: выходной
Васильев А.В.УРОЛОГПн-Сб: 8:00-15:00
+ Вс: выходной
Терентьев А.Т.КАРДИОЛОГПн-Ср: 10:00-13:00
+ Чт-Вс: 10:00-12:00
Богомолов Е. Е.ОТОЛАРИНГОЛОГПн-Сб: 10:00-18:00
+ Вс: выходной
Ольгина А. Г.ОФТАЛЬМОЛОГПн-Сб: 10:00-18:00
+ Вс: выходной
Ефремов Е. А.ПСИХОЛОГПн-Чт: 10:00-14:00
+ Пт-Сб: 12:00-14:00
+ Вс: выходной
Умнова А. А.ХИРУРГПн-Ср: 10:00-13:00
+ Чт-Вс: 10:00-12:00
Карагонова В. П.ЭНДОКРИНОЛОГПн-Сб: 10:00-18:00
+ Вс: выходной
+
+
+
+ +
+ + + + + +
+
+ + + + + +
\ No newline at end of file diff --git a/client/src/app/components/doctors-list/doctors-list.component.scss b/client/src/app/components/doctors-list/doctors-list.component.scss new file mode 100644 index 0000000..6ed7419 --- /dev/null +++ b/client/src/app/components/doctors-list/doctors-list.component.scss @@ -0,0 +1,189 @@ +@import "../../../styles.scss"; + +.appointment-card-container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +.appointment-card-banner { + box-shadow: 0 4px 8px rgba(62, 96, 207, 0.1), + 0 0 10px rgba(131, 176, 235, 0.1); // Enhanced shadow + border: 1px solid $dark-button-bg; + padding: 20px; + border-radius: 10px; + margin-bottom: 20px; + display: flex; + align-items: center; + justify-content: space-between; +} + +.banner-content { + flex: 1; + text-align: center; +} + + +.banner-content i { + margin-bottom: 10px; // Space between the icon and the heading + color: #007bff; // Color of the icon +} + +.banner-image { + flex-basis: 30%; // Adjust as needed + padding-left: 20px; // Space between text and image +} + +.banner-image img { + max-width: 100%; + border-radius: 10px; // Optional: to make the image corners rounded +} + +.doctorTimetable { + border: 1px dashed orange; + border-radius: 1rem; +} + +.table-card { + border: 1px solid #007bff; + //background-color: rgba(255, 255, 255, 0.1); // Transparent background + border-radius: 10px; + padding: 20px; + margin-bottom: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + } + + .table-scroll { + max-height: 400px; // Set a maximum height + overflow-y: auto; // Enable vertical scrolling + margin-bottom: 20px; // Optional, for spacing + + table { + width: 100%; + border-collapse: separate; + border-spacing: 0; + margin-top: 10px; + } + + // Rest of your table styles... +} + + + .appointments-table + { + margin-top: 20px; + } + + table { + width: 100%; + border-collapse: separate; // Change to separate + border-spacing: 0; // Add this to ensure there's no spacing between cells + margin-top: 10px; + } + + table th, + table td { + padding: 12px 10px; // Increased padding for better spacing + text-align: left; + position: relative; // To position the pseudo-element + } + + table tr:not(:last-child) th, + table tr:not(:last-child) { + border-bottom: 1px solid rgba(224, 224, 224, 0.5); // Lighter and semi-transparent border + } + + table th { + background-color: rgba( + 0, + 123, + 255, + 0.1 + ); // A very light blue background for headers + border-radius: 10px; // Rounded corners for the top + } + + .tabs { + display: flex; + margin-bottom: 10px; + border-radius: 10px; // Added corner radius for the container + overflow: hidden; // Ensures inner buttons conform to the container's border-radius + } + + .tabs button { + flex: 1; + padding: 10px; + cursor: pointer; + border: none; + background-color: rgba( + 0, + 123, + 255, + 0.1 + ); // Light blue background for better visibility + transition: background-color 0.3s ease; + border-radius: 5px; // Corner radius for individual buttons + color: #007bff; // Blue text color for better visibility + } + + .tabs button.active { + background-color: $dark-button-bg; + color: $dark-text; // Darker text color for active tab + } + + + .appointment-actions { + display: flex; + justify-content: space-between; // Changed to space-between for better spacing + margin-top: 20px; + flex-wrap: wrap; // Allows buttons to wrap on smaller screens + + @media (min-width: 768px) { // For tablets and desktops + justify-content: space-between; // Buttons at the edges of the card + } + } + + .btn-appointment { + padding: 10px 20px; + border-radius: 5px; + background-color: $light-button-bg; + color: $light-button-text; + border: none; + cursor: pointer; + transition: background-color 0.3s ease; + flex-grow: 1; // Allows buttons to grow and fill the space + + &:not(:last-child) { + margin-bottom: 10px; // Adds space between buttons on mobile + } + + &:hover { + background-color: darken($light-button-bg, 10%); + } + + @media (min-width: 768px) { // For tablets and desktops + flex-grow: 0; // Prevents buttons from growing + &:not(:last-child) { + margin-bottom: 0; // Removes the bottom margin on larger screens + margin-right: 10px; // Adds space between buttons + } + } + } + + + /* Responsive Design */ +@media (max-width: 768px) { + .appointment-card-banner { + flex-direction: column; + } + + .banner-content { + margin-bottom: 20px; + } + + .banner-image { + flex-basis: 100%; + padding-left: 0; + } + } + \ No newline at end of file diff --git a/client/src/app/components/doctors-list/doctors-list.component.ts b/client/src/app/components/doctors-list/doctors-list.component.ts new file mode 100644 index 0000000..4385172 --- /dev/null +++ b/client/src/app/components/doctors-list/doctors-list.component.ts @@ -0,0 +1,22 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-doctors-list', + templateUrl: './doctors-list.component.html', + styleUrls: ['./doctors-list.component.scss'], +}) +export class DoctorsList { + constructor(private router: Router) {} + + onDoctorClick(doctorName: string, speciality: string) { + this.router.navigate([ + '/schedule-an-appointment', + { doctorName, speciality }, + ]); + } + + onReturnToAppointments() { + this.router.navigate(['/appointments']); + } +} diff --git a/client/src/app/components/medical-card/medical-card.component.html b/client/src/app/components/medical-card/medical-card.component.html index f2c6493..2403ba8 100644 --- a/client/src/app/components/medical-card/medical-card.component.html +++ b/client/src/app/components/medical-card/medical-card.component.html @@ -15,36 +15,180 @@

Электронная медицинская карта

-
-

Личная информация

-
+
+

Личная информация

+ + +
- Иван Иванов + {{ profileData?.firstName }} {{ profileData?.middleName }} {{ profileData?.lastName }}
- 01.01.1990 + {{ profileData?.dateOfBirth }}
- Мужской + {{ profileData?.gender }}
- Москва, ул. Ленина, д. 10 + {{ profileData?.address }}
- +7 900 123 4567 + {{ profileData?.phoneNumber }}
+
+ + {{ profileData?.email }} +
+
- - ivanov@example.com +
-
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+ +
+

снимок МРТ

+ +
+ +
+

Результаты анализов

+ + + + + + + + + + Все + +
+ +
+ {{ card.dateOfShouldReady | date: 'dd-MM-yyyy' }} +

{{ card.name }}

+ {{ card.description }} +
+ +
+
+ + + + + Готово + +
+ +
+ {{ card.dateOfShouldReady | date: 'dd-MM-yyyy' }} +

{{ card.name }}

+ {{ card.description }} +
+ +
+
+ + + + + Не Готово + +
+ +
+ {{ card.dateOfShouldReady | date: 'dd-MM-yyyy' }} +

{{ card.name }}

+ {{ card.description }} +
+ +
+
+
+ + + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + + +
+
+
@@ -126,11 +270,10 @@

Медицинская история

- -
+ +
@@ -212,96 +355,12 @@

Диагностика

- + --> - -
-

Лечение и Рецепты

-
- -
- -
- 2023 -

Антибиотики для лечения инфекции

- Принимать дважды в день в течение 7 дней. -
-
-
- -
- 2022 -

Витамины для укрепления иммунитета

- Принимать один раз в день. -
-
-
- -
- 2021 -

Антигистаминные препараты

- Принимать при первых признаках аллергии. -
-
-
- -
- 2020 -

Лечение артрита

- Принимать по рекомендации врача. -
-
-
- -
- 2019 -

Лечение гастрита

- Соблюдать диету и принимать препараты. -
-
-
- -
- 2018 -

Лечение бронхита

- Принимать антибиотики и отдыхать. -
-
-
- -
- 2017 -

Лечение депрессии

- Консультации у психотерапевта и принимать антидепрессанты. -
-
-
- -
- 2016 -

Лечение ожогов

- Использовать мази и избегать солнечного света. -
-
-
- -
- 2015 -

Лечение гипертонии

- Принимать антигипертензивные препараты. -
-
-
- -
- 2014 -

Лечение диабета

- Соблюдать диету и принимать инсулин. -
-
-
-
+ + +
diff --git a/client/src/app/components/medical-card/medical-card.component.scss b/client/src/app/components/medical-card/medical-card.component.scss index a365275..21ef942 100644 --- a/client/src/app/components/medical-card/medical-card.component.scss +++ b/client/src/app/components/medical-card/medical-card.component.scss @@ -39,59 +39,151 @@ } .med-card { - background-color: transparent; // Transparent background - border: 1px solid #007bff; // Blue border + background-color: transparent; + border: 1px solid #007bff; padding: 20px; border-radius: 10px; margin-bottom: 20px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); } -// Personal Info .personal-info { box-shadow: 0 4px 8px rgba(62, 96, 207, 0.1), - 0 0 10px rgba(131, 176, 235, 0.1); + 0 0 10px rgba(131, 176, 235, 0.1); border: 1px solid $dark-button-bg; padding: 20px; border-radius: 10px; margin-bottom: 20px; display: flex; flex-direction: column; - align-items: flex-start; // Aligns the heading to the left/start - i { - color: $dark-button-bg; // Blue color for the icons + align-items: flex-start; + position: relative; + + h2 { + display: flex; + align-items: center; + gap: 10px; + } + + .info-content { + display: flex; + flex-direction: column; // Stack vertically + width: 100%; + margin-top: 20px; + } + + i{ + color: $dark-button-bg; + } + + .info-item { + margin: 10px 0; + display: flex; + justify-content: space-between; + align-items: center; + } + + .edit-button { + position: absolute; + top: 20px; + right: 20px; + background: none; + border: none; + cursor: pointer; + color: $dark-button-bg; + } + + label { + font-weight: bold; + display: flex; + align-items: center; + gap: 5px; } } -h2 { - display: flex; - align-items: center; - gap: 10px; // Space between the icon and the text +.save-button { + padding: 5px 10px; + background-color: $dark-button-bg; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + + &:hover { + background-color: darken($dark-button-bg, 10%); + } } -.info-content { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - width: 100%; - margin-top: 20px; // Space between the heading and the content +@media (max-width: 768px) { + .personal-info { + .edit-button { + top: 10px; // Adjust the top position + right: 10px; // Adjust the right position + } + + .info-item { + flex-direction: column; // Stack label and data vertically + align-items: flex-start; + } + + label, .save-button { + margin-bottom: 10px; // Add space between elements + } + } + + } -.info-item { - flex-basis: 48%; // Two items per row - margin: 10px 0; - display: flex; - justify-content: space-between; - align-items: center; + +.mri-image-container { + padding: 20px; + border-radius: 10px; + margin-bottom: 20px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + background-color: transparent; + border: 1px solid $dark-button-bg; + img { + max-width: 100%; // Ensure the image fits the container + height: auto; // Maintain aspect ratio + border-radius: 10px; + } } -label { - font-weight: bold; - display: flex; - align-items: center; - gap: 5px; // Space between the icon and the label text +// Add styles for your form here +.profile-edit-form { + width: 100%; + + .form-group { + margin-bottom: 15px; + + label { + display: block; + margin-bottom: 5px; + } + + input, select { + width: 100%; + padding: 10px; + border: 1px solid #ccc; + border-radius: 5px; + } + } + + .save-button { + padding: 10px 20px; + background-color: $dark-button-bg; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + + &:hover { + background-color: darken($dark-button-bg, 10%); + } + } } + .med-history-card { padding: 20px; border-radius: 10px; @@ -169,120 +261,149 @@ label { } } + .med-diagnostics-card { padding: 20px; border-radius: 10px; margin-bottom: 20px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - background-color: transparent; border: 1px solid $dark-button-bg; position: relative; - z-index: 1; // to ensure it pops up above other content -} + //background-color: #fff; -.diagnostics { h2 { - text-align: left; // Aligning the heading to the left/start + text-align: left; + margin-bottom: 10px; } - .diagnostics-content { - max-height: 300px; - overflow-y: auto; - padding-left: 40px; // Added padding to accommodate the icons + .add-result-card-button { + background: #007bff; + color: white; + border: none; + border-radius: 5px; + padding: 5px 10px; + cursor: pointer; + font-size: 0.9em; + display: inline-block; + margin-bottom: 20px; + + &:hover { + background: darken(#007bff, 10%); + } + + i { + margin-right: 5px; + } + } + + + // Style for Tab Group + mat-tab-group { + max-height: 500px; // Limiting the height to make it scrollable + overflow-y: auto; // Enable vertical scrolling + + .all-cards-tab, .done-cards-tab, .not-done-cards-tab { + // Custom style for tab labels + font-weight: bold; + color: $dark-button-bg; // Replace with your desired color + } + .diagnostics-item { display: flex; align-items: center; margin-bottom: 10px; - position: relative; - z-index: 1; - i { - color: $dark-button-bg; - margin-right: 10px; - background-color: #fff; - border-radius: 50%; - padding: 5px; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); - position: relative; - z-index: 2; + .fa-file-medical-alt { + color: #007bff; + margin-right: 15px; + font-size: 1.5rem; } .diagnostics-details { - display: flex; - flex-direction: column; + flex-grow: 1; strong { font-weight: bold; } - p { - margin-top: 5px; + p, span { + margin: 0; + font-size: 0.9em; } span { - font-size: 0.9em; color: gray; } } + .download-button { + // Style the download button + background: #007bff; + color: white; + border: none; + border-radius: 5px; + padding: 5px 10px; + margin-left: 10px; // Space from other content + margin-right: 10px; + cursor: pointer; + font-size: 0.9em; + + &:hover { + background: darken(#007bff, 10%); + } + + i { + } + } } } -} -.med-treatments-card { - padding: 20px; - border-radius: 10px; - margin-bottom: 20px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); - background-color: transparent; - border: 1px solid $dark-button-bg; - position: relative; - z-index: 1; -} + .result-card-create-form, .result-card-edit-form { + border: 1px solid $dark-button-bg; + border-radius: 5px; + padding: 20px; + //background-color: #f8f9fa; -.treatments { - h2 { - text-align: left; - } + .form-group { + margin-bottom: 15px; - .treatments-content { - max-height: 300px; - overflow-y: auto; - padding-left: 30px; + label { + display: block; + margin-bottom: 5px; + } - .treatments-item { - display: flex; - align-items: center; - margin-bottom: 10px; - position: relative; - z-index: 1; + input[type="text"], input[type="date"], input[type="email"], textarea { + width: 100%; + padding: 8px; + border: 1px solid $dark-button-bg; + border-radius: 4px; + } - i { - color: $dark-button-bg; - margin-right: 10px; - background-color: #fff; - border-radius: 50%; - padding: 5px; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); - position: relative; - z-index: 2; + textarea { + resize: vertical; } + } - .treatments-details { - strong { - display: block; - font-weight: bold; - margin-bottom: 5px; - } + .btn { + margin-right: 10px; + } + } - p { - margin-bottom: 5px; - } - } + // Responsive adjustments + @media (max-width: 768px) { + .add-result-card-button { + width: 100%; + margin-bottom: 15px; + } + + .result-card-create-form, .result-card-edit-form { + padding: 10px; } } } + + .attachments { padding: 20px; border-radius: 10px; diff --git a/client/src/app/components/medical-card/medical-card.component.ts b/client/src/app/components/medical-card/medical-card.component.ts index c2e4936..92b0f84 100644 --- a/client/src/app/components/medical-card/medical-card.component.ts +++ b/client/src/app/components/medical-card/medical-card.component.ts @@ -1,10 +1,256 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ProfileService } from 'src/app/core/services/profile.service'; +import { ProfileData } from 'src/app/core/models/profile-data.model'; +import { ResultCard } from 'src/app/core/models/result-card.model'; +import { ToastrService } from 'ngx-toastr'; +import { ResultCardService } from 'src/app/core/services/result-card.service'; +import * as moment from 'moment'; +import { AuthService } from 'src/app/core/services/auth.service'; + @Component({ selector: 'app-medical-card', templateUrl: './medical-card.component.html', - styleUrls: ['./medical-card.component.scss'] + styleUrls: ['./medical-card.component.scss'], }) -export class MedicalCardComponent { +export class MedicalCardComponent implements OnInit { + profileForm!: FormGroup; + isEditing = false; + profileData!: ProfileData; // Define a property to hold the profile data + + resultCards: ResultCard[] = []; + doneResultCards: ResultCard[] = []; + notDoneResultCards: ResultCard[] = []; + isEditingResultCard = false; + resultCardForm!: FormGroup; // Add FormGroup for result card editing + createResultCardForm!: FormGroup; // Form for creating a new result card + isCreatingResultCard = false; // For showing the create result card form + selectedResultCard: ResultCard | null = null; // To hold the selected card for editing + + constructor( + private fb: FormBuilder, + private profileService: ProfileService, + private toastr: ToastrService, // Inject ToastrService + private resultCardService: ResultCardService, + private authService: AuthService + ) {} + + ngOnInit(): void { + this.initializeForm(); + this.loadProfile(); + this.loadAllUserCards(); + this.loadAllDoneUserCards(); + this.loadAllNotDoneUserCards(); + this.initializeCreateResultCardForm(); + this.resultCardService.getAllUserCards().subscribe({ + next: (response) => { + console.log(response); // Check the structure of the response + this.resultCards = response.resultCardList; // Assuming the response has a property 'resultCardList' + }, + error: (error) => console.error('Error fetching result cards:', error), + }); + } + + isShowingMRI = false; + mriImageUrl = ''; + + initializeForm(): void { + this.profileForm = this.fb.group({ + firstName: [''], // Add default values as needed + middleName: [''], // Add default values as needed + lastName: [''], // Add default values as needed + dateOfBirth: [''], // Add default values as needed + gender: [''], // Add default values as needed + address: [''], // Add default values as needed + phoneNumber: [''], // Add default values as needed + mriImageUrl: [''], + }); + } + + + + // In MedicalCardComponent + initializeCreateResultCardForm(): void { + const userEmail = this.authService.getCurrentUserEmail(); + this.createResultCardForm = this.fb.group({ + name: ['', Validators.required], + description: ['', Validators.required], + dateOfShouldReady: ['', Validators.required], // Use a suitable format (e.g., 'YYYY-MM-DD') + hospitalAddress: ['', Validators.required], + userEmail: [userEmail, [Validators.required, Validators.email]] + }); + } + + loadProfile(): void { + this.profileService.getProfile().subscribe({ + next: (profile) => { + console.log(profile, 'prof'); + if (profile) { + this.profileForm.patchValue(profile); + // Add the email to profileData + this.profileData = { ...profile, email: this.authService.getCurrentUserEmail() || '' }; + } else { + this.toastr.info('Пожалуйста, заполните информацию в вашем профиле.'); + // Redirect to profile completion page if needed + } + }, + error: (error) => { + // Handle errors if needed + }, + }); + } + + + toggleEdit(): void { + this.isEditing = !this.isEditing; + if (!this.isEditing) { + // Reload profile data when exiting edit mode + this.loadProfile(); + } + } + + saveProfile(): void { + if (this.profileForm.valid) { + // Format the dateOfBirth field + const formattedProfileData = { + ...this.profileForm.value, + dateOfBirth: this.profileForm.value.dateOfBirth + ? moment(this.profileForm.value.dateOfBirth).format('DD/MM/YYYY') + : '' + }; + + this.profileService.saveProfile(formattedProfileData).subscribe(() => { + this.toggleEdit(); + this.loadProfile(); // Reload the profile data to reflect the changes + this.toastr.success('Profile updated successfully'); + }, error => { + this.toastr.error('Failed to update profile'); + console.error('Error updating profile:', error); + }); + } + } + + + + createResultCard(): void { + if (this.createResultCardForm.valid) { + // Format the date to match the backend's expected format + const formattedDate = moment(this.createResultCardForm.value.dateOfShouldReady).format('DD-MM-YYYY'); + + // Create a new object with the formatted date + const formData = { + ...this.createResultCardForm.value, + dateOfShouldReady: formattedDate + }; + + this.resultCardService.createResultCard(formData).subscribe({ + next: (newResultCard) => { + this.toastr.success('Result card created successfully'); + this.toggleCreateResultCardForm(); // Close the form and reload the list + this.loadAllUserCards(); // Reload the cards list + }, + error: (error) => { + this.toastr.error('Failed to create result card'); + console.error('Error creating result card:', error); + } + }); + } else { + this.toastr.error('Please fill in all required fields'); + } + } + + + + toggleCreateResultCardForm(): void { + console.log("Toggling Create Result Card Form"); + this.isCreatingResultCard = !this.isCreatingResultCard; + if (!this.isCreatingResultCard) { + console.log("Loading user cards..."); + this.loadAllUserCards(); // This should be triggered when the form is closed + } + } + + loadAllUserCards() { + this.resultCardService.getAllUserCards().subscribe({ + next: (response) => (this.resultCards = response.resultCardList), + error: (error) => console.error('Error fetching result cards:', error), + }); + } + + loadAllDoneUserCards() { + this.resultCardService.getAllDoneUserCards().subscribe({ + next: (response) => (this.doneResultCards = response.resultCardList), + error: (error) => + console.error('Error fetching done result cards:', error), + }); + } + + loadAllNotDoneUserCards() { + this.resultCardService.getAllNotDoneUserCards().subscribe({ + next: (response) => (this.notDoneResultCards = response.resultCardList), + error: (error) => + console.error('Error fetching not done result cards:', error), + }); + } + + // In MedicalCardComponent + +downloadResultFile(cardId: number): void { + this.resultCardService.downloadResultFile(cardId).subscribe(blob => { + // Create a new Blob object + const file = new Blob([blob], { type: 'application/pdf' }); // Adjust the type as necessary + + // Create a URL for the file + const fileURL = URL.createObjectURL(file); + + // Create a temporary anchor element and trigger the download + const anchor = document.createElement('a'); + anchor.href = fileURL; + anchor.download = `result-card-${cardId}.pdf`; // Set the filename as you wish + document.body.appendChild(anchor); + anchor.click(); + + // Cleanup: remove the anchor and revoke the URL + document.body.removeChild(anchor); + URL.revokeObjectURL(fileURL); + }, error => { + console.error('Error downloading file:', error); + this.toastr.error('Failed to download file'); + }); +} + + + + + async onAddMRI(): Promise { + const pickerOpts = { + types: [ + { + description: 'Images', + accept: { + 'image/*': ['.png', '.gif', '.jpeg', '.jpg'], + }, + }, + ], + excludeAcceptAllOption: true, + multiple: false, + }; + + let fileHandle; + + [fileHandle] = await (window as any).showOpenFilePicker(pickerOpts); + const fileData = await fileHandle.getFile(); + this.mriImageUrl = window.URL.createObjectURL(fileData); + // this.profileService + // .saveProfile({ + // ...this.profileData, + // mriImageUrl: window.URL.createObjectURL(fileData), + // }) + // .subscribe(() => { + // this.toggleEdit(); + // this.loadProfile(); // Reload the profile data to reflect the changes + // }); + } } diff --git a/client/src/app/core/models/profile-data.model.ts b/client/src/app/core/models/profile-data.model.ts new file mode 100644 index 0000000..bcf7aef --- /dev/null +++ b/client/src/app/core/models/profile-data.model.ts @@ -0,0 +1,20 @@ +// profile-data.model.ts + +export interface ProfileData { + firstName: string; + middleName: string; + lastName: string; + address: string; + dateOfBirth: string; // Ensure this is in 'dd/MM/yyyy' format + phoneNumber: string; + gender: Gender; + email: string; + mriImageUrl: string; +} + +export enum Gender { + MALE = 'Мужской', + FEMALE = 'Женский', + // ... other gender values as per your backend ... +} + diff --git a/client/src/app/core/models/result-card-response.model.ts b/client/src/app/core/models/result-card-response.model.ts new file mode 100644 index 0000000..f857a94 --- /dev/null +++ b/client/src/app/core/models/result-card-response.model.ts @@ -0,0 +1,6 @@ +// result-card-response.model.ts +import { ResultCard } from './result-card.model'; + +export interface ResultCardResponse { + resultCardList: ResultCard[]; +} diff --git a/client/src/app/core/models/result-card.model.ts b/client/src/app/core/models/result-card.model.ts new file mode 100644 index 0000000..0ce48f9 --- /dev/null +++ b/client/src/app/core/models/result-card.model.ts @@ -0,0 +1,21 @@ +// result-card.model.ts +export interface ResultCard { + id: number; + name: string; + description: string; + dateOfMake: Date; + dateOfShouldReady: Date; + dateOfDelivered: Date; + hospitalAddress: string; + resultFileId?: number; + userId: number; + } + + // Add this interface in your models +export interface CreateResultCardRequest { + name: string; + description: string; + dateOfShouldReady: string; + hospitalAddress: string; + userEmail: string; +} \ No newline at end of file diff --git a/client/src/app/core/models/result-file.model.ts b/client/src/app/core/models/result-file.model.ts new file mode 100644 index 0000000..d10490e --- /dev/null +++ b/client/src/app/core/models/result-file.model.ts @@ -0,0 +1,9 @@ +// result-file.model.ts +export interface ResultFile { + id: number; + name: string; + originalFileName: string; + size: number; + contentType: string; + bytes: Blob; + } \ No newline at end of file diff --git a/client/src/app/core/services/auth.service.ts b/client/src/app/core/services/auth.service.ts index 6b2a4dd..33413ba 100644 --- a/client/src/app/core/services/auth.service.ts +++ b/client/src/app/core/services/auth.service.ts @@ -3,6 +3,8 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { environment } from 'src/environments/environment'; +import { jwtDecode } from "jwt-decode"; + @Injectable({ providedIn: 'root', @@ -37,6 +39,16 @@ export class AuthService { logout(): void { localStorage.removeItem('authToken'); } + + getCurrentUserEmail(): string | null { + const token = this.getAuthToken(); + if (token) { + const decodedToken: any = jwtDecode(token); + //console.log("Decoded Token:", decodedToken); + return decodedToken.sub; // Assuming 'email' is a field in the token payload + } + return null; + } } diff --git a/client/src/app/core/services/profile.service.ts b/client/src/app/core/services/profile.service.ts new file mode 100644 index 0000000..bec0610 --- /dev/null +++ b/client/src/app/core/services/profile.service.ts @@ -0,0 +1,33 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { environment } from 'src/environments/environment'; +import { AuthService } from './auth.service'; +import { ProfileData } from '../models/profile-data.model'; + + + +@Injectable({ + providedIn: 'root', +}) +export class ProfileService { + private baseUrl = environment.apiUrl; + + constructor(private http: HttpClient, private authService: AuthService) { } + + getProfile(): Observable { + const headers = this.createAuthorizationHeader(); + return this.http.get(`${this.baseUrl}/profile`, { headers }); + } + + saveProfile(profileData: ProfileData): Observable { + const headers = this.createAuthorizationHeader(); + return this.http.post(`${this.baseUrl}/profile`, profileData, { headers }); + } + + private createAuthorizationHeader(): HttpHeaders { + return new HttpHeaders({ + 'Authorization': `Bearer ${this.authService.getAuthToken()}` + }); + } +} diff --git a/client/src/app/core/services/result-card.service.ts b/client/src/app/core/services/result-card.service.ts new file mode 100644 index 0000000..9bcb80f --- /dev/null +++ b/client/src/app/core/services/result-card.service.ts @@ -0,0 +1,55 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { AuthService } from './auth.service'; +import { CreateResultCardRequest, ResultCard } from '../models/result-card.model'; +import { ResultFile } from '../models/result-file.model'; +import { environment } from 'src/environments/environment'; +import { ResultCardResponse } from '../models/result-card-response.model'; + +@Injectable({ providedIn: 'root' }) +export class ResultCardService { + private apiBaseUrl = `${environment.apiUrl}/result-card`; + private filesApiUrl = `${environment.apiUrl}/files`; + + constructor(private http: HttpClient, private authService: AuthService) {} + + private getHeaders(): HttpHeaders { + const token = this.authService.getAuthToken(); + return new HttpHeaders({ 'Authorization': `Bearer ${token}` }); + } + + // createResultCard(resultCardData: CreateResultCardRequest): Observable { + // return this.http.post(`${this.apiBaseUrl}/create`, resultCardData, { headers: this.getHeaders() }); + // } + + createResultCard(resultCardData: CreateResultCardRequest): Observable { + const fullUrl = `${this.apiBaseUrl}/create`; + console.log('Full URL:', fullUrl); + + return this.http.post(fullUrl, resultCardData, { headers: this.getHeaders() }); + } + + + viewResultCard(cardId: number): Observable { + return this.http.get(`${this.filesApiUrl}/result-file/${cardId}`, { headers: this.getHeaders() }); + } + + downloadResultFile(cardId: number): Observable { + return this.http.post(`${this.apiBaseUrl}/download`, { cardId }, { headers: this.getHeaders(), responseType: 'blob' as 'json' }); + } + + getAllUserCards(): Observable { + return this.http.get(`${environment.apiUrl}/all-cards`, { headers: this.getHeaders() }); + } + + getAllDoneUserCards(): Observable { + return this.http.get(`${environment.apiUrl}/all-done-cards`, { headers: this.getHeaders() }); + } + + getAllNotDoneUserCards(): Observable { + return this.http.get(`${environment.apiUrl}/all-not-done-cards`, { headers: this.getHeaders() }); + } + + // Additional methods as needed... +} diff --git a/client/src/app/modules/home/home.component.html b/client/src/app/modules/home/home.component.html index a375199..34a54aa 100644 --- a/client/src/app/modules/home/home.component.html +++ b/client/src/app/modules/home/home.component.html @@ -6,7 +6,7 @@ - + \ No newline at end of file diff --git a/client/src/app/shared/components/navbar/navbar.component.html b/client/src/app/shared/components/navbar/navbar.component.html index 9746977..2521afe 100644 --- a/client/src/app/shared/components/navbar/navbar.component.html +++ b/client/src/app/shared/components/navbar/navbar.component.html @@ -22,7 +22,7 @@ Электронная карта @@ -31,11 +31,11 @@ - diff --git a/client/src/app/shared/shared.module.ts b/client/src/app/shared/shared.module.ts index 22574b9..54f52b8 100644 --- a/client/src/app/shared/shared.module.ts +++ b/client/src/app/shared/shared.module.ts @@ -3,15 +3,15 @@ import { CommonModule } from '@angular/common'; import { FooterComponent } from './components/footer/footer.component'; import { NavbarComponent } from './components/navbar/navbar.component'; import { RouterModule } from '@angular/router'; - - +import { AppointmentsComponent } from '../components/appointments/appointments.component'; @NgModule({ schemas: [ CUSTOM_ELEMENTS_SCHEMA ], declarations: [ FooterComponent, - NavbarComponent + NavbarComponent, + AppointmentsComponent ], imports: [ CommonModule, @@ -22,4 +22,4 @@ import { RouterModule } from '@angular/router'; NavbarComponent ] }) -export class SharedModule { } +export class SharedModule {} diff --git a/client/src/styles.scss b/client/src/styles.scss index 0e4157a..4f1116e 100644 --- a/client/src/styles.scss +++ b/client/src/styles.scss @@ -1,5 +1,6 @@ /* You can add global styles to this file, and also import other style files */ @import "styles/_variables.scss"; +@import "~@angular/material/prebuilt-themes/indigo-pink.css"; a { text-decoration: none; // Removes the underline from links @@ -149,7 +150,7 @@ body.dark-theme { } ::-webkit-scrollbar { - width: 12px; + width: 5px; } ::-webkit-scrollbar-track {