Oviya commited on
Commit
463dbc3
·
1 Parent(s): bc7457a

all update

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .editorconfig +16 -0
  2. .gitignore +44 -0
  3. .vscode/extensions.json +4 -0
  4. .vscode/launch.json +19 -0
  5. .vscode/tasks.json +42 -0
  6. CHANGELOG.md +16 -0
  7. Py-Detect.esproj +16 -0
  8. Py-Detect.esproj.user +8 -0
  9. Py-Detect.sln +27 -0
  10. Py-Detect.vbproj +84 -0
  11. angular.json +106 -0
  12. app.config +25 -0
  13. karma.conf.js +44 -0
  14. obj/Debug/AngularProject1.esproj.CoreCompileInputs.cache +0 -0
  15. obj/Debug/AngularProject1.esproj.FileListAbsolute.txt +4 -0
  16. obj/Debug/Py-Detect.esproj.CoreCompileInputs.cache +0 -0
  17. obj/Debug/Py-Detect.esproj.FileListAbsolute.txt +1 -0
  18. obj/Debug/package.g.props +39 -0
  19. obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache +0 -0
  20. obj/x86/Debug/Py-Detect.vbproj.AssemblyReference.cache +0 -0
  21. obj/x86/Release/Py-Detect.vbproj.AssemblyReference.cache +0 -0
  22. package-lock.json +0 -0
  23. package.json +42 -0
  24. postcss.config.js +6 -0
  25. postcss.config.js.txt +0 -0
  26. src/app/app-routing.module.ts +37 -0
  27. src/app/app.component.css +0 -0
  28. src/app/app.component.html +5 -0
  29. src/app/app.component.spec.ts +27 -0
  30. src/app/app.component.ts +10 -0
  31. src/app/app.module.ts +38 -0
  32. src/app/case-details-page/case-details-page.component.css +330 -0
  33. src/app/case-details-page/case-details-page.component.html +107 -0
  34. src/app/case-details-page/case-details-page.component.spec.ts +21 -0
  35. src/app/case-details-page/case-details-page.component.ts +40 -0
  36. src/app/case-store.service.ts +104 -0
  37. src/app/homepage/homepage.component.css +1118 -0
  38. src/app/homepage/homepage.component.html +121 -0
  39. src/app/homepage/homepage.component.spec.ts +21 -0
  40. src/app/homepage/homepage.component.ts +59 -0
  41. src/app/homepage/sign-in/sign-in.component.css +243 -0
  42. src/app/homepage/sign-in/sign-in.component.html +45 -0
  43. src/app/homepage/sign-in/sign-in.component.spec.ts +21 -0
  44. src/app/homepage/sign-in/sign-in.component.ts +55 -0
  45. src/app/homepage/sign-in/sign-in.service.spec.ts +16 -0
  46. src/app/homepage/sign-in/sign-in.service.ts +15 -0
  47. src/app/homepage/sign-up/sign-up.component.css +245 -0
  48. src/app/homepage/sign-up/sign-up.component.html +98 -0
  49. src/app/homepage/sign-up/sign-up.component.spec.ts +21 -0
  50. src/app/homepage/sign-up/sign-up.component.ts +117 -0
.editorconfig ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Editor configuration, see https://editorconfig.org
2
+ root = true
3
+
4
+ [*]
5
+ charset = utf-8
6
+ indent_style = space
7
+ indent_size = 2
8
+ insert_final_newline = true
9
+ trim_trailing_whitespace = true
10
+
11
+ [*.ts]
12
+ quote_type = single
13
+
14
+ [*.md]
15
+ max_line_length = off
16
+ trim_trailing_whitespace = false
.gitignore ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # See http://help.github.com/ignore-files/ for more about ignoring files.
2
+
3
+ # Compiled output
4
+ /dist
5
+ /tmp
6
+ /out-tsc
7
+ /bazel-out
8
+
9
+ # Node
10
+ /node_modules
11
+ npm-debug.log
12
+ yarn-error.log
13
+
14
+ # IDEs and editors
15
+ .idea/
16
+ .project
17
+ .classpath
18
+ .c9/
19
+ *.launch
20
+ .settings/
21
+ *.sublime-workspace
22
+
23
+ # Visual Studio Code
24
+ .vscode/*
25
+ !.vscode/settings.json
26
+ !.vscode/tasks.json
27
+ !.vscode/launch.json
28
+ !.vscode/extensions.json
29
+ .history/*
30
+ .vs
31
+
32
+ # Miscellaneous
33
+ /.angular/cache
34
+ .sass-cache/
35
+ /connect.lock
36
+ /coverage
37
+ /libpeerconnection.log
38
+ testem.log
39
+ /typings
40
+
41
+ # System files
42
+ .DS_Store
43
+ Thumbs.db
44
+ .vs
.vscode/extensions.json ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ {
2
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3
+ "recommendations": ["angular.ng-template"]
4
+ }
.vscode/launch.json ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "type": "edge",
6
+ "request": "launch",
7
+ "name": "localhost (Edge)",
8
+ "url": "http://localhost:51272",
9
+ "webRoot": "${workspaceFolder}"
10
+ },
11
+ {
12
+ "type": "chrome",
13
+ "request": "launch",
14
+ "name": "localhost (Chrome)",
15
+ "url": "http://localhost:51272",
16
+ "webRoot": "${workspaceFolder}"
17
+ }
18
+ ]
19
+ }
.vscode/tasks.json ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3
+ "version": "2.0.0",
4
+ "tasks": [
5
+ {
6
+ "type": "npm",
7
+ "script": "start",
8
+ "isBackground": true,
9
+ "problemMatcher": {
10
+ "owner": "typescript",
11
+ "pattern": "$tsc",
12
+ "background": {
13
+ "activeOnStart": true,
14
+ "beginsPattern": {
15
+ "regexp": "(.*?)"
16
+ },
17
+ "endsPattern": {
18
+ "regexp": "bundle generation complete"
19
+ }
20
+ }
21
+ }
22
+ },
23
+ {
24
+ "type": "npm",
25
+ "script": "test",
26
+ "isBackground": true,
27
+ "problemMatcher": {
28
+ "owner": "typescript",
29
+ "pattern": "$tsc",
30
+ "background": {
31
+ "activeOnStart": true,
32
+ "beginsPattern": {
33
+ "regexp": "(.*?)"
34
+ },
35
+ "endsPattern": {
36
+ "regexp": "bundle generation complete"
37
+ }
38
+ }
39
+ }
40
+ }
41
+ ]
42
+ }
CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This file explains how Visual Studio created the project.
2
+
3
+ The following tools were used to generate this project:
4
+ - Angular CLI (ng)
5
+
6
+ The following steps were used to generate this project:
7
+ - Create Angular project with ng: `ng new PyDetect --defaults --skip-install --skip-git `.
8
+ - Update angular.json with port.
9
+ - Create project file (`AngularProject1.esproj`).
10
+ - Create `launch.json` to enable debugging.
11
+ - Update package.json to add `jest-editor-support`.
12
+ - Update `start` script in `package.json` to specify host.
13
+ - Add `karma.conf.js` for unit tests.
14
+ - Update `angular.json` to point to `karma.conf.js`.
15
+ - Add project to solution.
16
+ - Write this file.
Py-Detect.esproj ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <Project Sdk="Microsoft.VisualStudio.JavaScript.Sdk/1.0.2752196">
2
+ <PropertyGroup>
3
+ <StartupCommand>npm start</StartupCommand>
4
+ <JavaScriptTestFramework>Jasmine</JavaScriptTestFramework>
5
+ <!-- Allows the build (or compile) script located on package.json to run on Build -->
6
+ <ShouldRunBuildScript>false</ShouldRunBuildScript>
7
+ <!-- Folder where production build objects will be placed -->
8
+ <BuildOutputFolder>$(MSBuildProjectDirectory)\dist\Py-Detect\</BuildOutputFolder>
9
+ </PropertyGroup>
10
+ <ItemGroup>
11
+ <None Remove="postcss.config.js.txt" />
12
+ </ItemGroup>
13
+ <ItemGroup>
14
+ <None Include="Py-Detect.esproj" />
15
+ </ItemGroup>
16
+ </Project>
Py-Detect.esproj.user ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
4
+ <DebuggerFlavor>LaunchJsonDebugger</DebuggerFlavor>
5
+ <LaunchJsonTarget>
6
+ </LaunchJsonTarget>
7
+ </PropertyGroup>
8
+ </Project>
Py-Detect.sln ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 
2
+ Microsoft Visual Studio Solution File, Format Version 12.00
3
+ # Visual Studio Version 17
4
+ VisualStudioVersion = 17.14.36414.22 d17.14
5
+ MinimumVisualStudioVersion = 10.0.40219.1
6
+ Project("{54A90642-561A-4BB1-A94E-469ADEE60C69}") = "Py-Detect", "Py-Detect.esproj", "{83D35911-4668-16FC-F96B-AF2EBA027839}"
7
+ EndProject
8
+ Global
9
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
10
+ Debug|Any CPU = Debug|Any CPU
11
+ Release|Any CPU = Release|Any CPU
12
+ EndGlobalSection
13
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
14
+ {83D35911-4668-16FC-F96B-AF2EBA027839}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15
+ {83D35911-4668-16FC-F96B-AF2EBA027839}.Debug|Any CPU.Build.0 = Debug|Any CPU
16
+ {83D35911-4668-16FC-F96B-AF2EBA027839}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
17
+ {83D35911-4668-16FC-F96B-AF2EBA027839}.Release|Any CPU.ActiveCfg = Release|Any CPU
18
+ {83D35911-4668-16FC-F96B-AF2EBA027839}.Release|Any CPU.Build.0 = Release|Any CPU
19
+ {83D35911-4668-16FC-F96B-AF2EBA027839}.Release|Any CPU.Deploy.0 = Release|Any CPU
20
+ EndGlobalSection
21
+ GlobalSection(SolutionProperties) = preSolution
22
+ HideSolutionNode = FALSE
23
+ EndGlobalSection
24
+ GlobalSection(ExtensibilityGlobals) = postSolution
25
+ SolutionGuid = {B10E018F-46C2-409C-8CB2-631740B0CB75}
26
+ EndGlobalSection
27
+ EndGlobal
Py-Detect.vbproj ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3
+ <PropertyGroup>
4
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
6
+ <ProjectGuid>{B0E7CDD1-5D44-4320-BCBC-D63ACCC09351}</ProjectGuid>
7
+ <OutputType>Exe</OutputType>
8
+ <StartupObject>
9
+ </StartupObject>
10
+ <RootNamespace>
11
+ </RootNamespace>
12
+ <AssemblyName>ConsoleApplication</AssemblyName>
13
+ <FileAlignment>512</FileAlignment>
14
+ <MyType>Console</MyType>
15
+ <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
16
+ <TargetFrameworkProfile>
17
+ </TargetFrameworkProfile>
18
+ </PropertyGroup>
19
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
20
+ <DebugSymbols>true</DebugSymbols>
21
+ <DebugType>full</DebugType>
22
+ <DefineDebug>true</DefineDebug>
23
+ <DefineTrace>true</DefineTrace>
24
+ <OutputPath>bin\Debug\</OutputPath>
25
+ <NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
26
+ <PlatformTarget>x86</PlatformTarget>
27
+ <Prefer32Bit>false</Prefer32Bit>
28
+ </PropertyGroup>
29
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
30
+ <DebugType>pdbonly</DebugType>
31
+ <DefineDebug>false</DefineDebug>
32
+ <DefineTrace>true</DefineTrace>
33
+ <Optimize>true</Optimize>
34
+ <OutputPath>bin\Release\</OutputPath>
35
+ <NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
36
+ <PlatformTarget>x86</PlatformTarget>
37
+ <Prefer32Bit>false</Prefer32Bit>
38
+ </PropertyGroup>
39
+ <PropertyGroup>
40
+ <OptionExplicit>On</OptionExplicit>
41
+ </PropertyGroup>
42
+ <PropertyGroup>
43
+ <OptionCompare>Binary</OptionCompare>
44
+ </PropertyGroup>
45
+ <PropertyGroup>
46
+ <OptionStrict>Off</OptionStrict>
47
+ </PropertyGroup>
48
+ <PropertyGroup>
49
+ <OptionInfer>On</OptionInfer>
50
+ </PropertyGroup>
51
+ <ItemGroup>
52
+ <Reference Include="System" />
53
+ <Reference Include="System.Data" />
54
+ <Reference Include="System.Deployment" />
55
+ <Reference Include="System.Xml" />
56
+ <Reference Include="System.Core" />
57
+ <Reference Include="System.Xml.Linq" />
58
+ <Reference Include="System.Data.DataSetExtensions" />
59
+ </ItemGroup>
60
+ <ItemGroup>
61
+ <Import Include="Microsoft.VisualBasic" />
62
+ <Import Include="System" />
63
+ <Import Include="System.Collections" />
64
+ <Import Include="System.Collections.Generic" />
65
+ <Import Include="System.Data" />
66
+ <Import Include="System.Diagnostics" />
67
+ <Import Include="System.Linq" />
68
+ <Import Include="System.Xml.Linq" />
69
+ </ItemGroup>
70
+ <ItemGroup>
71
+ <None Include="app.config" />
72
+ </ItemGroup>
73
+ <Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.Targets" />
74
+ <ProjectExtensions>
75
+ <VisualStudio AllowExistingFolder="true" />
76
+ </ProjectExtensions>
77
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
78
+ Other similar extension points exist, see Microsoft.Common.targets.
79
+ <Target Name="BeforeBuild">
80
+ </Target>
81
+ <Target Name="AfterBuild">
82
+ </Target>
83
+ -->
84
+ </Project>
angular.json ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
3
+ "version": 1,
4
+ "newProjectRoot": "projects",
5
+ "projects": {
6
+ "PyDetect": {
7
+ "projectType": "application",
8
+ "schematics": {},
9
+ "root": "",
10
+ "sourceRoot": "src",
11
+ "prefix": "app",
12
+ "architect": {
13
+ "build": {
14
+ "builder": "@angular-devkit/build-angular:browser",
15
+ "options": {
16
+ "outputPath": "dist/py-detect",
17
+ "index": "src/index.html",
18
+ "main": "src/main.ts",
19
+ "polyfills": [
20
+ "zone.js"
21
+ ],
22
+ "tsConfig": "tsconfig.app.json",
23
+ "assets": [
24
+ "src/favicon.ico",
25
+ "src/assets"
26
+ ],
27
+ "styles": [
28
+ "src/styles.css"
29
+ ],
30
+ "scripts": []
31
+ },
32
+ "configurations": {
33
+ "production": {
34
+ "budgets": [
35
+ {
36
+ "type": "initial",
37
+ "maximumWarning": "500kb",
38
+ "maximumError": "1mb"
39
+ },
40
+ {
41
+ "type": "anyComponentStyle",
42
+ "maximumWarning": "20kb",
43
+ "maximumError": "20kb"
44
+ }
45
+ ],
46
+ "outputHashing": "all"
47
+ },
48
+
49
+ "development": {
50
+ "buildOptimizer": false,
51
+ "optimization": false,
52
+ "vendorChunk": true,
53
+ "extractLicenses": false,
54
+ "sourceMap": true,
55
+ "namedChunks": true
56
+ }
57
+ },
58
+ "defaultConfiguration": "production"
59
+ },
60
+ "serve": {
61
+ "builder": "@angular-devkit/build-angular:dev-server",
62
+ "configurations": {
63
+ "production": {
64
+ "browserTarget": "PyDetect:build:production"
65
+ },
66
+ "development": {
67
+ "browserTarget": "PyDetect:build:development"
68
+ }
69
+ },
70
+ "defaultConfiguration": "development",
71
+ "options": {
72
+ "port": 51272
73
+ }
74
+ },
75
+ "extract-i18n": {
76
+ "builder": "@angular-devkit/build-angular:extract-i18n",
77
+ "options": {
78
+ "browserTarget": "PyDetect:build"
79
+ }
80
+ },
81
+ "test": {
82
+ "builder": "@angular-devkit/build-angular:karma",
83
+ "options": {
84
+ "polyfills": [
85
+ "zone.js",
86
+ "zone.js/testing"
87
+ ],
88
+ "tsConfig": "tsconfig.spec.json",
89
+ "assets": [
90
+ "src/favicon.ico",
91
+ "src/assets"
92
+ ],
93
+ "styles": [
94
+ "src/styles.css"
95
+ ],
96
+ "scripts": [],
97
+ "karmaConfig": "karma.conf.js"
98
+ }
99
+ }
100
+ }
101
+ }
102
+ },
103
+ "cli": {
104
+ "analytics": "26ed0fe9-fe22-459c-80d4-3f1e393fedc1"
105
+ }
106
+ }
app.config ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <configuration>
3
+
4
+ <system.diagnostics>
5
+ <sources>
6
+ <!-- This section defines the logging configuration for My.Application.Log -->
7
+ <source name="DefaultSource" switchName="DefaultSwitch">
8
+ <listeners>
9
+ <add name="FileLog"/>
10
+ <!-- Uncomment the below section to write to the Application Event Log -->
11
+ <!--<add name="EventLog"/>-->
12
+ </listeners>
13
+ </source>
14
+ </sources>
15
+ <switches>
16
+ <add name="DefaultSwitch" value="Information"/>
17
+ </switches>
18
+ <sharedListeners>
19
+ <add name="FileLog" type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" initializeData="FileLogWriter"/>
20
+ <!-- Uncomment the below section and replace APPLICATION_NAME with the name of your application to write to the Application Event Log -->
21
+ <!--<add name="EventLog" type="System.Diagnostics.EventLogTraceListener" initializeData="APPLICATION_NAME"/> -->
22
+ </sharedListeners>
23
+ </system.diagnostics>
24
+
25
+ <startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>
karma.conf.js ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = function (config) {
2
+ config.set({
3
+ basePath: '',
4
+ frameworks: ['jasmine', '@angular-devkit/build-angular'],
5
+ plugins: [
6
+ require('karma-jasmine'),
7
+ require('karma-chrome-launcher'),
8
+ require('karma-jasmine-html-reporter'),
9
+ require('karma-coverage'),
10
+ require('@angular-devkit/build-angular/plugins/karma')
11
+ ],
12
+ client: {
13
+ jasmine: {
14
+ // you can add configuration options for Jasmine here
15
+ // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
16
+ // for example, you can disable the random execution with `random: false`
17
+ // or set a specific seed with `seed: 4321`
18
+ },
19
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
20
+ },
21
+ jasmineHtmlReporter: {
22
+ suppressAll: true // removes the duplicated traces
23
+ },
24
+ coverageReporter: {
25
+ dir: require('path').join(__dirname, './coverage/'),
26
+ subdir: '.',
27
+ reporters: [
28
+ { type: 'html' },
29
+ { type: 'text-summary' }
30
+ ]
31
+ },
32
+ reporters: ['progress', 'kjhtml'],
33
+ port: 9876,
34
+ colors: true,
35
+ logLevel: config.LOG_INFO,
36
+ autoWatch: true,
37
+ browsers: ['Chrome'],
38
+ singleRun: false,
39
+ restartOnFileChange: true,
40
+ listenAddress: 'localhost',
41
+ hostname: 'localhost'
42
+ });
43
+ };
44
+
obj/Debug/AngularProject1.esproj.CoreCompileInputs.cache ADDED
File without changes
obj/Debug/AngularProject1.esproj.FileListAbsolute.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ C:\Users\Admin\Desktop\AngularProject1\obj\Debug\AngularProject1.esproj.CoreCompileInputs.cache
2
+ C:\Users\Admin\Desktop\AngularProject1\AngularProject1\obj\Debug\AngularProject1.esproj.CoreCompileInputs.cache
3
+ C:\Users\Admin\Desktop\AngularProject1\PyDetect\obj\Debug\AngularProject1.esproj.CoreCompileInputs.cache
4
+ C:\Users\Admin\Desktop\AngularProject1\Py-Detect\obj\Debug\AngularProject1.esproj.CoreCompileInputs.cache
obj/Debug/Py-Detect.esproj.CoreCompileInputs.cache ADDED
File without changes
obj/Debug/Py-Detect.esproj.FileListAbsolute.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ C:\Users\Admin\Desktop\Py-Detect\obj\Debug\Py-Detect.esproj.CoreCompileInputs.cache
obj/Debug/package.g.props ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <Project>
3
+ <PropertyGroup>
4
+ <PackageJsonName Condition="$(PackageJsonName) == ''">Py-Detect</PackageJsonName>
5
+ <PackageJsonVersion Condition="$(PackageJsonVersion) == ''">0.0.0</PackageJsonVersion>
6
+ <PackageJsonScriptsNg Condition="$(PackageJsonScriptsNg) == ''">ng</PackageJsonScriptsNg>
7
+ <PackageJsonScriptsStart Condition="$(PackageJsonScriptsStart) == ''">ng serve --host=127.0.0.1</PackageJsonScriptsStart>
8
+ <PackageJsonScriptsBuild Condition="$(PackageJsonScriptsBuild) == ''">ng build</PackageJsonScriptsBuild>
9
+ <PackageJsonScriptsWatch Condition="$(PackageJsonScriptsWatch) == ''">ng build --watch --configuration development</PackageJsonScriptsWatch>
10
+ <PackageJsonScriptsTest Condition="$(PackageJsonScriptsTest) == ''">ng test</PackageJsonScriptsTest>
11
+ <PackageJsonPrivate Condition="$(PackageJsonPrivate) == ''">true</PackageJsonPrivate>
12
+ <PackageJsonDependenciesAngularAnimations Condition="$(PackageJsonDependenciesAngularAnimations) == ''">^16.1.0</PackageJsonDependenciesAngularAnimations>
13
+ <PackageJsonDependenciesAngularCommon Condition="$(PackageJsonDependenciesAngularCommon) == ''">^16.1.0</PackageJsonDependenciesAngularCommon>
14
+ <PackageJsonDependenciesAngularCompiler Condition="$(PackageJsonDependenciesAngularCompiler) == ''">^16.1.0</PackageJsonDependenciesAngularCompiler>
15
+ <PackageJsonDependenciesAngularCore Condition="$(PackageJsonDependenciesAngularCore) == ''">^16.1.0</PackageJsonDependenciesAngularCore>
16
+ <PackageJsonDependenciesAngularForms Condition="$(PackageJsonDependenciesAngularForms) == ''">^16.1.0</PackageJsonDependenciesAngularForms>
17
+ <PackageJsonDependenciesAngularPlatformBrowser Condition="$(PackageJsonDependenciesAngularPlatformBrowser) == ''">^16.1.0</PackageJsonDependenciesAngularPlatformBrowser>
18
+ <PackageJsonDependenciesAngularPlatformBrowserDynamic Condition="$(PackageJsonDependenciesAngularPlatformBrowserDynamic) == ''">^16.1.0</PackageJsonDependenciesAngularPlatformBrowserDynamic>
19
+ <PackageJsonDependenciesAngularRouter Condition="$(PackageJsonDependenciesAngularRouter) == ''">^16.1.0</PackageJsonDependenciesAngularRouter>
20
+ <PackageJsonDependenciesJestEditorSupport Condition="$(PackageJsonDependenciesJestEditorSupport) == ''">*</PackageJsonDependenciesJestEditorSupport>
21
+ <PackageJsonDependenciesRxjs Condition="$(PackageJsonDependenciesRxjs) == ''">~7.8.0</PackageJsonDependenciesRxjs>
22
+ <PackageJsonDependenciesTslib Condition="$(PackageJsonDependenciesTslib) == ''">^2.3.0</PackageJsonDependenciesTslib>
23
+ <PackageJsonDependenciesZoneJs Condition="$(PackageJsonDependenciesZoneJs) == ''">~0.13.0</PackageJsonDependenciesZoneJs>
24
+ <PackageJsonDevdependenciesAngularDevkitBuildAngular Condition="$(PackageJsonDevdependenciesAngularDevkitBuildAngular) == ''">^16.1.0</PackageJsonDevdependenciesAngularDevkitBuildAngular>
25
+ <PackageJsonDevdependenciesAngularCli Condition="$(PackageJsonDevdependenciesAngularCli) == ''">~16.1.0</PackageJsonDevdependenciesAngularCli>
26
+ <PackageJsonDevdependenciesAngularCompilerCli Condition="$(PackageJsonDevdependenciesAngularCompilerCli) == ''">^16.1.0</PackageJsonDevdependenciesAngularCompilerCli>
27
+ <PackageJsonDevdependenciesTypesJasmine Condition="$(PackageJsonDevdependenciesTypesJasmine) == ''">~4.3.0</PackageJsonDevdependenciesTypesJasmine>
28
+ <PackageJsonDevdependenciesAutoprefixer Condition="$(PackageJsonDevdependenciesAutoprefixer) == ''">^10.4.21</PackageJsonDevdependenciesAutoprefixer>
29
+ <PackageJsonDevdependenciesJasmineCore Condition="$(PackageJsonDevdependenciesJasmineCore) == ''">~4.6.0</PackageJsonDevdependenciesJasmineCore>
30
+ <PackageJsonDevdependenciesKarma Condition="$(PackageJsonDevdependenciesKarma) == ''">~6.4.0</PackageJsonDevdependenciesKarma>
31
+ <PackageJsonDevdependenciesKarmaChromeLauncher Condition="$(PackageJsonDevdependenciesKarmaChromeLauncher) == ''">~3.2.0</PackageJsonDevdependenciesKarmaChromeLauncher>
32
+ <PackageJsonDevdependenciesKarmaCoverage Condition="$(PackageJsonDevdependenciesKarmaCoverage) == ''">~2.2.0</PackageJsonDevdependenciesKarmaCoverage>
33
+ <PackageJsonDevdependenciesKarmaJasmine Condition="$(PackageJsonDevdependenciesKarmaJasmine) == ''">~5.1.0</PackageJsonDevdependenciesKarmaJasmine>
34
+ <PackageJsonDevdependenciesKarmaJasmineHtmlReporter Condition="$(PackageJsonDevdependenciesKarmaJasmineHtmlReporter) == ''">~2.1.0</PackageJsonDevdependenciesKarmaJasmineHtmlReporter>
35
+ <PackageJsonDevdependenciesPostcss Condition="$(PackageJsonDevdependenciesPostcss) == ''">^8.5.6</PackageJsonDevdependenciesPostcss>
36
+ <PackageJsonDevdependenciesTailwindcss Condition="$(PackageJsonDevdependenciesTailwindcss) == ''">^3.4.17</PackageJsonDevdependenciesTailwindcss>
37
+ <PackageJsonDevdependenciesTypescript Condition="$(PackageJsonDevdependenciesTypescript) == ''">~5.1.3</PackageJsonDevdependenciesTypescript>
38
+ </PropertyGroup>
39
+ </Project>
obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache ADDED
Binary file (1.52 kB). View file
 
obj/x86/Debug/Py-Detect.vbproj.AssemblyReference.cache ADDED
Binary file (3.83 kB). View file
 
obj/x86/Release/Py-Detect.vbproj.AssemblyReference.cache ADDED
Binary file (3.83 kB). View file
 
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Py-Detect",
3
+ "version": "0.0.0",
4
+ "scripts": {
5
+ "ng": "ng",
6
+ "start": "ng serve --host=127.0.0.1",
7
+ "build": "ng build",
8
+ "watch": "ng build --watch --configuration development",
9
+ "test": "ng test"
10
+ },
11
+ "private": true,
12
+ "dependencies": {
13
+ "@angular/animations": "^16.1.0",
14
+ "@angular/common": "^16.1.0",
15
+ "@angular/compiler": "^16.1.0",
16
+ "@angular/core": "^16.1.0",
17
+ "@angular/forms": "^16.1.0",
18
+ "@angular/platform-browser": "^16.1.0",
19
+ "@angular/platform-browser-dynamic": "^16.1.0",
20
+ "@angular/router": "^16.1.0",
21
+ "jest-editor-support": "*",
22
+ "rxjs": "~7.8.0",
23
+ "tslib": "^2.3.0",
24
+ "zone.js": "~0.13.0"
25
+ },
26
+ "devDependencies": {
27
+ "@angular-devkit/build-angular": "^16.1.0",
28
+ "@angular/cli": "~16.1.0",
29
+ "@angular/compiler-cli": "^16.1.0",
30
+ "@types/jasmine": "~4.3.0",
31
+ "autoprefixer": "^10.4.21",
32
+ "jasmine-core": "~4.6.0",
33
+ "karma": "~6.4.0",
34
+ "karma-chrome-launcher": "~3.2.0",
35
+ "karma-coverage": "~2.2.0",
36
+ "karma-jasmine": "~5.1.0",
37
+ "karma-jasmine-html-reporter": "~2.1.0",
38
+ "postcss": "^8.5.6",
39
+ "tailwindcss": "^3.4.17",
40
+ "typescript": "~5.1.3"
41
+ }
42
+ }
postcss.config.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
postcss.config.js.txt ADDED
File without changes
src/app/app-routing.module.ts ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NgModule } from '@angular/core';
2
+ import { RouterModule, Routes } from '@angular/router';
3
+ import { HomepageComponent } from './homepage/homepage.component';
4
+ import { PyDetectComponent } from './py-detect/py-detect.component';
5
+ import { InfopageComponent } from './infopage/infopage.component';
6
+ import { ValidationpageComponent } from './validationpage/validationpage.component';
7
+ import { RecordpageComponent } from './recordpage/recordpage.component';
8
+ import { CaseDetailsPageComponent } from './case-details-page/case-details-page.component';
9
+
10
+ const routes: Routes = [
11
+ { path: '', component: HomepageComponent },
12
+ { path: 'infopage', component: InfopageComponent },
13
+ { path: 'infopage/:id', component: InfopageComponent },
14
+ { path: 'py-detect', component: PyDetectComponent },
15
+ { path: 'validationpage', component: ValidationpageComponent },
16
+ { path: 'record', component: RecordpageComponent },
17
+ { path: 'case-details', component: CaseDetailsPageComponent },
18
+ { path: 'case-details/:id', component: CaseDetailsPageComponent },
19
+
20
+ {
21
+ path: 'auth/signin', loadComponent: () =>
22
+ import('./homepage/sign-in/sign-in.component').then(m => m.SignInComponent)
23
+ },
24
+ {
25
+ path: 'auth/signup', loadComponent: () =>
26
+ import('./homepage/sign-up/sign-up.component').then(m => m.SignUpComponent)
27
+ },
28
+
29
+ { path: '**', redirectTo: '' }
30
+ ];
31
+
32
+ @NgModule({
33
+ imports: [RouterModule.forRoot(routes)],
34
+ exports: [RouterModule]
35
+ })
36
+ export class AppRoutingModule { }
37
+
src/app/app.component.css ADDED
File without changes
src/app/app.component.html ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ <!--
2
+ <app-sample></app-sample>
3
+ -->
4
+ <router-outlet></router-outlet>
5
+
src/app/app.component.spec.ts ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { AppComponent } from './app.component';
3
+
4
+ describe('AppComponent', () => {
5
+ beforeEach(() => TestBed.configureTestingModule({
6
+ declarations: [AppComponent]
7
+ }));
8
+
9
+ it('should create the app', () => {
10
+ const fixture = TestBed.createComponent(AppComponent);
11
+ const app = fixture.componentInstance;
12
+ expect(app).toBeTruthy();
13
+ });
14
+
15
+ it(`should have as title 'PyDetect'`, () => {
16
+ const fixture = TestBed.createComponent(AppComponent);
17
+ const app = fixture.componentInstance;
18
+ expect(app.title).toEqual('PyDetect');
19
+ });
20
+
21
+ it('should render title', () => {
22
+ const fixture = TestBed.createComponent(AppComponent);
23
+ fixture.detectChanges();
24
+ const compiled = fixture.nativeElement as HTMLElement;
25
+ expect(compiled.querySelector('.content span')?.textContent).toContain('PyDetect app is running!');
26
+ });
27
+ });
src/app/app.component.ts ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'app-root',
5
+ templateUrl: './app.component.html',
6
+ styleUrls: ['./app.component.css']
7
+ })
8
+ export class AppComponent {
9
+ title = 'PyDetect';
10
+ }
src/app/app.module.ts ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { NgModule } from '@angular/core';
2
+ import { BrowserModule } from '@angular/platform-browser';
3
+ import { ReactiveFormsModule, FormsModule } from '@angular/forms';
4
+ import { CommonModule } from '@angular/common';
5
+ import { HttpClientModule } from '@angular/common/http';
6
+ import { AppComponent } from './app.component';
7
+ import { InfopageComponent } from './infopage/infopage.component';
8
+ import { AppRoutingModule } from './app-routing.module';
9
+ import { ValidationpageComponent } from './validationpage/validationpage.component';
10
+ import { SignInComponent } from './homepage/sign-in/sign-in.component';
11
+ import { SignUpComponent } from './homepage/sign-up/sign-up.component';
12
+ import { RecordpageComponent } from './recordpage/recordpage.component';
13
+ import { HomepageComponent } from './homepage/homepage.component';
14
+ import { CaseDetailsPageComponent } from './case-details-page/case-details-page.component';
15
+
16
+ @NgModule({
17
+ declarations: [
18
+ AppComponent,
19
+ InfopageComponent,
20
+ ValidationpageComponent,
21
+ RecordpageComponent,
22
+ CaseDetailsPageComponent
23
+ ],
24
+ imports: [
25
+ BrowserModule,
26
+ ReactiveFormsModule,
27
+ FormsModule,
28
+ CommonModule,
29
+ AppRoutingModule,
30
+ HttpClientModule,
31
+ HomepageComponent,
32
+ SignInComponent,
33
+ SignUpComponent
34
+ ],
35
+ providers: [],
36
+ bootstrap: [AppComponent]
37
+ })
38
+ export class AppModule { }
src/app/case-details-page/case-details-page.component.css ADDED
@@ -0,0 +1,330 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import '../recordpage/recordpage.component.css';
2
+
3
+ .case-details-list {
4
+ width: 100%;
5
+ max-width: 1700px;
6
+ margin: 0 auto;
7
+ background: #fff;
8
+ border-radius: 8px;
9
+ box-shadow: 0 4px 32px rgba(30,41,59,0.12);
10
+ padding: 32px 48px;
11
+ }
12
+
13
+ .case-details-item {
14
+ border-bottom: 1px solid #edf2f7;
15
+ padding: 24px 0;
16
+ }
17
+
18
+ .case-details-item:last-child {
19
+ border-bottom: none;
20
+ }
21
+
22
+ h2 {
23
+ color: #38bdf8;
24
+ font-size: 2.1rem;
25
+ font-weight: 800;
26
+ margin-bottom: 32px;
27
+ text-align: center;
28
+ }
29
+
30
+ h3 {
31
+ color: #23272b;
32
+ font-size: 1.2rem;
33
+ font-weight: 700;
34
+ margin-bottom: 12px;
35
+ }
36
+
37
+ p {
38
+ margin: 4px 0;
39
+ color: #334155;
40
+ }
41
+
42
+ hr {
43
+ margin: 24px 0 0 0;
44
+ border: none;
45
+ border-top: 1px solid #e2e8f0;
46
+ }
47
+
48
+ .welcome-user {
49
+ position: absolute;
50
+ top: 24px;
51
+ left: 32px;
52
+ font-size: 1.1rem;
53
+ color: #ffffff;
54
+ font-weight: 600;
55
+ z-index: 2;
56
+ }
57
+
58
+ .welcome-user-fixed {
59
+ position: absolute;
60
+ top: 24px;
61
+ left: 32px;
62
+ font-size: 1.1rem;
63
+ color: #334155;
64
+ font-weight: 600;
65
+ z-index: 2;
66
+ }
67
+
68
+ .page-title-centered {
69
+ text-align: center;
70
+ margin-top: 24px;
71
+ font-size: 2.1rem;
72
+ font-weight: 800;
73
+ color: #38bdf8;
74
+ }
75
+
76
+ .wrapper.records-center {
77
+ position: relative;
78
+ }
79
+
80
+ .welcome-left {
81
+ text-align: left;
82
+ font-size: 2.1rem;
83
+ font-weight: 800;
84
+ color: #38bdf8;
85
+ margin-bottom: 32px;
86
+ margin-top: 0;
87
+ }
88
+
89
+ .table-wrap {
90
+ position: relative;
91
+ }
92
+
93
+ .welcome-left {
94
+ font-size: 2.1rem;
95
+ font-weight: 800;
96
+ color: #38bdf8;
97
+ margin-bottom: 16px;
98
+ text-align: left;
99
+ position: relative;
100
+ right: 32vw;
101
+ }
102
+
103
+ .modal-case-details {
104
+ background: #f8fbfd;
105
+ border-radius: 18px;
106
+ box-shadow: 0 20px 60px rgba(0,0,0,.25);
107
+ padding: 0;
108
+ max-width: 540px;
109
+ min-width: 340px;
110
+ }
111
+
112
+ .modal-header-case {
113
+ background: #f5fafd;
114
+ border-bottom: 1px solid #e2e8f0;
115
+ padding: 24px 24px 12px 24px;
116
+ }
117
+
118
+ .modal-header-case h2 {
119
+ font-size: 1.6rem;
120
+ font-weight: 800;
121
+ color: #23272b;
122
+ margin: 0;
123
+ }
124
+
125
+ .modal-body-case {
126
+ padding: 18px 24px 8px 24px;
127
+ background: #f8fbfd;
128
+ }
129
+
130
+ .modal-footer-case {
131
+ background: #f5fafd;
132
+ border-top: 1px solid #e2e8f0;
133
+ padding: 16px 24px;
134
+ text-align: left;
135
+ }
136
+
137
+ .detail-row {
138
+ display: grid;
139
+ grid-template-columns: 160px 1fr;
140
+ gap: 8px;
141
+ padding: 6px 0;
142
+ color: #334155;
143
+ }
144
+
145
+ .detail-row span {
146
+ color: #64748b;
147
+ font-weight: 500;
148
+ }
149
+
150
+ .detail-row b {
151
+ color: #23272b;
152
+ font-weight: 500;
153
+ }
154
+
155
+ .bold-value {
156
+ font-weight: 700 !important;
157
+ color: #23272b !important;
158
+ }
159
+
160
+ .detail-block .label {
161
+ font-weight: 700;
162
+ margin-bottom: 6px;
163
+ color: #23272b;
164
+ }
165
+
166
+ hr {
167
+ margin: 18px 0 12px 0;
168
+ border: none;
169
+ border-top: 1px solid #e2e8f0;
170
+ }
171
+
172
+ /* Modal for case details (match record page style) */
173
+ .modal-blur-overlay {
174
+ position: fixed;
175
+ top: 0;
176
+ left: 0;
177
+ width: 100%;
178
+ height: 100%;
179
+ background: rgba(0, 0, 0, 0.5);
180
+ z-index: 1000;
181
+ }
182
+
183
+ .modal {
184
+ background: #fefefe;
185
+ border-radius: 8px;
186
+ box-shadow: 0 4px 32px rgba(30, 41, 59, 0.12);
187
+ overflow: hidden;
188
+ position: fixed;
189
+ top: 50%;
190
+ left: 50%;
191
+ transform: translate(-50%, -50%);
192
+ z-index: 1001;
193
+ width: 90%;
194
+ max-width: 600px;
195
+ display: flex;
196
+ flex-direction: column;
197
+ animation: fadeIn 0.3s ease-out;
198
+ }
199
+
200
+ .modal-header {
201
+ background: #f5fafd;
202
+ border-bottom: 1px solid #e2e8f0;
203
+ padding: 16px;
204
+ display: flex;
205
+ justify-content: space-between;
206
+ align-items: center;
207
+ }
208
+
209
+ .modal-header h2 {
210
+ font-size: 1.4rem;
211
+ font-weight: 700;
212
+ margin: 0;
213
+ color: #23272b;
214
+ }
215
+
216
+ .modal-body {
217
+ padding: 16px;
218
+ background: #fff;
219
+ flex: 1;
220
+ overflow-y: auto;
221
+ }
222
+
223
+ .modal-footer {
224
+ background: #f5fafd;
225
+ border-top: 1px solid #e2e8f0;
226
+ padding: 12px;
227
+ text-align: right;
228
+ }
229
+
230
+ .btn {
231
+ background: #38bdf8;
232
+ color: #fff;
233
+ padding: 10px 20px;
234
+ border: none;
235
+ border-radius: 4px;
236
+ font-size: 1rem;
237
+ cursor: pointer;
238
+ transition: background 0.3s;
239
+ }
240
+
241
+ .btn:hover {
242
+ background: #34b3e0;
243
+ }
244
+
245
+ @keyframes fadeIn {
246
+ from {
247
+ opacity: 0;
248
+ transform: translate(-50%, -48%);
249
+ }
250
+ to {
251
+ opacity: 1;
252
+ transform: translate(-50%, -50%);
253
+ }
254
+ }
255
+
256
+ .active-cases-label {
257
+ font-size: 1.8rem;
258
+ font-weight: 800;
259
+ color: #38bdf8;
260
+ margin-bottom: 16px;
261
+ position: relative;
262
+ text-align: left;
263
+ }
264
+
265
+ .actions {
266
+ display: flex;
267
+ flex-direction: row;
268
+ justify-content: center;
269
+ align-items: center;
270
+ gap: 16px;
271
+ flex-wrap: nowrap;
272
+ width: 100%;
273
+ }
274
+
275
+ .actions .btn {
276
+ min-width: 150px;
277
+ max-width: 200px;
278
+ padding: 12px 20px;
279
+ font-size: 1.08rem;
280
+ white-space: nowrap;
281
+ border-radius: 8px;
282
+ box-sizing: border-box;
283
+ overflow: visible;
284
+ text-overflow: unset;
285
+ margin: 0 2px;
286
+ }
287
+
288
+ .actions-col {
289
+ text-align: center;
290
+ vertical-align: middle;
291
+ }
292
+
293
+ .records th.actions-col,
294
+ .records td.actions {
295
+ width: 28% !important;
296
+ min-width: 260px;
297
+ max-width: 320px;
298
+ }
299
+
300
+ @media (max-width: 700px) {
301
+ .actions .btn {
302
+ min-width: 120px;
303
+ max-width: 160px;
304
+ font-size: 1rem;
305
+ padding: 8px 10px;
306
+ }
307
+ .records th.actions-col,
308
+ .records td.actions {
309
+ min-width: 140px;
310
+ max-width: 180px;
311
+ width: 36% !important;
312
+ }
313
+ }
314
+
315
+ /* Make all table columns equal width */
316
+ .records {
317
+ table-layout: fixed;
318
+ width: 100%;
319
+ }
320
+ .records th,
321
+ .records td {
322
+ width: 16.66%; /* 6 columns, 100/6 = 16.66% */
323
+ text-align: center;
324
+ vertical-align: middle;
325
+ white-space: nowrap;
326
+ overflow: hidden;
327
+ text-overflow: ellipsis;
328
+ }
329
+
330
+
src/app/case-details-page/case-details-page.component.html ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="searchbar-topright">
2
+ <form class="modern-searchbar-form compact white-bg">
3
+ <span class="modern-searchbar-icon">
4
+ <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#64748b" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
5
+ <circle cx="11" cy="11" r="8"/>
6
+ <line x1="21" y1="21" x2="16.65" y2="16.65"/>
7
+ </svg>
8
+ </span>
9
+ <input type="text" class="modern-searchbar-input white-bg" placeholder="Search... (not active)" autocomplete="off" />
10
+ </form>
11
+ </div>
12
+
13
+ <div class="logo-title-row">
14
+ <img src="/assets/pykara-logo.png" alt="PyDetect Logo" class="logo-img" style="cursor:pointer;" (click)="navigateHome()" />
15
+ <div class="py-detect-title">
16
+ <span class="py-letter p">P</span>
17
+ <span class="py-letter y">Y</span>
18
+ <span class="py-shape"></span>
19
+ <span class="py-letter d">D</span>
20
+ <span class="py-letter e">E</span>
21
+ <span class="py-letter t">T</span>
22
+ <span class="py-letter e2">E</span>
23
+ <span class="py-letter c">C</span>
24
+ <span class="py-letter t2">T</span>
25
+ </div>
26
+ </div>
27
+
28
+ <div style="height:128px;"></div>
29
+
30
+ <main class="content" role="main">
31
+ <div class="wrapper records-center">
32
+ <div class="welcome-left">Welcome User 👮</div>
33
+ <div *ngIf="username" class="welcome-user">Welcome, {{ username }}👮</div>
34
+ <div class="active-cases-label left-align"><b>Your Active Cases</b></div>
35
+ <div class="table-wrap padded-table-wrap">
36
+ <table class="records">
37
+ <thead>
38
+ <tr>
39
+ <th>Case ID</th>
40
+ <th>Crime</th>
41
+ <th>Date &amp; Time</th>
42
+ <th>Location</th>
43
+ <th>Status</th>
44
+ <th class="actions-col">Actions</th>
45
+ </tr>
46
+ </thead>
47
+ <tbody>
48
+ <tr *ngFor="let c of cases">
49
+ <td class="mono">{{ c.caseId || '—' }}</td>
50
+ <td>{{ c.crime || '—' }}</td>
51
+ <td>{{ c.dateTime ? (c.dateTime | date:'yyyy-MM-dd HH:mm') : '—' }}</td>
52
+ <td class="ellipsis" [title]="c.police.address || ''">{{ c.police.address || '—' }}</td>
53
+ <td>
54
+ <span class="status"
55
+ [class.open]="c.status==='Open'"
56
+ [class.under]="c.status==='Under Investigation'"
57
+ [class.closed]="c.status==='Closed'">
58
+ {{ c.status || '—' }}
59
+ </span>
60
+ </td>
61
+ <td class="actions">
62
+ <button type="button" class="btn view" (click)="openDetails(c)">View Details</button>
63
+ <button type="button" class="btn edit" (click)="goToDetect()">Go Detect</button>
64
+ </td>
65
+ </tr>
66
+ <tr *ngIf="cases.length === 0">
67
+ <td colspan="6" class="empty">No records found.</td>
68
+ </tr>
69
+ </tbody>
70
+ </table>
71
+ </div>
72
+
73
+ <!-- Modal for case details -->
74
+ <!-- ...existing code above... -->
75
+ <!-- Modal for case details -->
76
+ <!-- ...existing code above... -->
77
+ <!-- Modal for case details (match record page style) -->
78
+ <div class="modal-blur-overlay" *ngIf="showDetails"></div>
79
+ <div class="modal" *ngIf="showDetails" role="dialog" aria-modal="true" aria-labelledby="detailsTitle">
80
+ <div class="modal-header">
81
+ <h2 id="detailsTitle">Case Details</h2>
82
+ </div>
83
+ <div class="modal-body" *ngIf="selectedCase as sc">
84
+ <div class="detail-row"><span>Case ID</span><b>{{ sc.caseId || '—' }}</b></div>
85
+ <div class="detail-row"><span>Crime</span><b>{{ sc.crime || '—' }}</b></div>
86
+ <div class="detail-row"><span>Date & Time</span><b>{{ sc.dateTime ? (sc.dateTime | date:'yyyy-MM-dd HH:mm') : '—' }}</b></div>
87
+ <div class="detail-row"><span>Location</span><b>{{ sc.police.address || '—' }}</b></div>
88
+ <hr />
89
+ <div class="detail-row"><span>Investigation Officer</span><b>{{ sc.police.name || '—' }}</b></div>
90
+ <div class="detail-row"><span>Duty Person</span><b>{{ sc.police.dutyPerson || '—' }}</b></div>
91
+ <div class="detail-row"><span>Mode of Crime</span><b>{{ sc.police.modeOfCrime || '—' }}</b></div>
92
+ <hr />
93
+ <div class="detail-row"><span>Accused Name</span><b>{{ sc.accused.name || '—' }}</b></div>
94
+ <div class="detail-row"><span>Accused Gender</span><b>{{ sc.accused.gender || '—' }}</b></div>
95
+ <div class="detail-row"><span>Accused Age</span><b>{{ sc.accused.age || '—' }}</b></div>
96
+ <div class="detail-row"><span>Accused Address</span><b>{{ sc.accused.address || '—' }}</b></div>
97
+ <hr />
98
+ <div class="detail-block">
99
+ <div class="label">Notes / Explanation</div>
100
+ <p class="explain">{{ sc.police.information || '—' }}</p>
101
+ </div>
102
+ </div>
103
+ <div class="modal-footer">
104
+ <button type="button" class="btn" (click)="closeDetails()">Close</button>
105
+ </div>
106
+ </div>
107
+
src/app/case-details-page/case-details-page.component.spec.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { CaseDetailsPageComponent } from './case-details-page.component';
4
+
5
+ describe('CaseDetailsPageComponent', () => {
6
+ let component: CaseDetailsPageComponent;
7
+ let fixture: ComponentFixture<CaseDetailsPageComponent>;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({
11
+ declarations: [CaseDetailsPageComponent]
12
+ });
13
+ fixture = TestBed.createComponent(CaseDetailsPageComponent);
14
+ component = fixture.componentInstance;
15
+ fixture.detectChanges();
16
+ });
17
+
18
+ it('should create', () => {
19
+ expect(component).toBeTruthy();
20
+ });
21
+ });
src/app/case-details-page/case-details-page.component.ts ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component, OnInit } from '@angular/core';
2
+ import { Router } from '@angular/router';
3
+ import { CaseStoreService, PoliceCase } from '../shared/case-store.service';
4
+
5
+ @Component({
6
+ selector: 'app-case-details-page',
7
+ templateUrl: './case-details-page.component.html',
8
+ styleUrls: ['./case-details-page.component.css']
9
+ })
10
+ export class CaseDetailsPageComponent implements OnInit {
11
+ cases: PoliceCase[] = [];
12
+ showDetails = false;
13
+ selectedCase: PoliceCase | null = null;
14
+ username: string = '';
15
+
16
+ constructor(private caseStore: CaseStoreService, private router: Router) {}
17
+
18
+ ngOnInit(): void {
19
+ this.cases = this.caseStore.getPoliceCases();
20
+ this.username = localStorage.getItem('username') || sessionStorage.getItem('username') || '';
21
+ }
22
+
23
+ openDetails(c: PoliceCase): void {
24
+ this.selectedCase = c;
25
+ this.showDetails = true;
26
+ }
27
+
28
+ closeDetails(): void {
29
+ this.showDetails = false;
30
+ this.selectedCase = null;
31
+ }
32
+
33
+ goToDetect(): void {
34
+ window.location.href = '/py-detect';
35
+ }
36
+
37
+ navigateHome(): void {
38
+ this.router.navigate(['/']);
39
+ }
40
+ }
src/app/case-store.service.ts ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Injectable } from '@angular/core';
2
+
3
+ export interface PoliceCase {
4
+ // Optional metadata used by your UI
5
+ caseId?: string;
6
+ dateTime?: string;
7
+ status?: 'Open' | 'Under Investigation' | 'Closed';
8
+ crime: string;
9
+ police: {
10
+ name: string;
11
+ station: string;
12
+ address: string;
13
+ pincode: string;
14
+ dutyPerson: string;
15
+ modeOfCrime: string;
16
+ information?: string;
17
+ };
18
+ accused: {
19
+ name: string;
20
+ age: string | number;
21
+ gender: string;
22
+ address: string;
23
+ occupation?: string;
24
+ };
25
+ }
26
+
27
+ @Injectable({ providedIn: 'root' })
28
+ export class CaseStoreService {
29
+ private readonly storageKey = 'py_detect_police_cases';
30
+ private cases: PoliceCase[] = [];
31
+
32
+ constructor() {
33
+ this.load();
34
+ }
35
+
36
+ /** Create (newest first) */
37
+ addPoliceCase(c: PoliceCase): void {
38
+ this.cases.unshift(c);
39
+ this.save();
40
+ }
41
+
42
+ /** Read */
43
+ getPoliceCases(): PoliceCase[] {
44
+ return this.cases;
45
+ }
46
+
47
+ /** Update by array index */
48
+ updatePoliceCaseAt(index: number, updated: PoliceCase): void {
49
+ if (index >= 0 && index < this.cases.length) {
50
+ this.cases[index] = updated;
51
+ this.save();
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Convenience: map Info page reactive-form value to PoliceCase and store it.
57
+ * Call with the whole this.form.value from InfopageComponent.
58
+ */
59
+ addFromInfoForm(formValue: any): void {
60
+ const crime = (formValue && formValue.crime) || {};
61
+ const suspect = (formValue && formValue.suspect) || {};
62
+ const notes = (formValue && formValue.notes) || {};
63
+
64
+ const mapped: PoliceCase = {
65
+ caseId: crime.caseId || '',
66
+ dateTime: crime.dateTime || '',
67
+ status: notes.status || 'Open',
68
+ crime: crime.crimeType || 'Unknown',
69
+ police: {
70
+ name: notes.officerInCharge || '—',
71
+ station: '—', // not captured on Info page
72
+ address: crime.location || '—',
73
+ pincode: '', // not captured on Info page
74
+ dutyPerson: notes.officerInCharge || '—',
75
+ modeOfCrime: crime.crimeType || '—',
76
+ information: notes.initialFindings || ''
77
+ },
78
+ accused: {
79
+ name: suspect.fullName || '—',
80
+ age: suspect.age || '—',
81
+ gender: suspect.gender || '—',
82
+ address: suspect.address || '—',
83
+ occupation: suspect.alias || ''
84
+ }
85
+ };
86
+
87
+ this.addPoliceCase(mapped);
88
+ }
89
+
90
+ /** Persist to localStorage (safe to keep; remove if not needed) */
91
+ private save(): void {
92
+ try { localStorage.setItem(this.storageKey, JSON.stringify(this.cases)); } catch { }
93
+ }
94
+
95
+ /** Load from localStorage */
96
+ private load(): void {
97
+ try {
98
+ const raw = localStorage.getItem(this.storageKey);
99
+ this.cases = raw ? (JSON.parse(raw) as PoliceCase[]) : [];
100
+ } catch {
101
+ this.cases = [];
102
+ }
103
+ }
104
+ }
src/app/homepage/homepage.component.css ADDED
@@ -0,0 +1,1118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body, html {
2
+ margin: 0;
3
+ padding: 0;
4
+ font-family: 'Arial', 'Segoe UI', 'Roboto', sans-serif;
5
+ background: url('/assets/background.jpg') no-repeat center center fixed !important;
6
+ background-size: cover !important;
7
+ }
8
+
9
+ .homepage-container {
10
+ display: flex;
11
+ flex-direction: row;
12
+ height: 100vh;
13
+ color: #e3f6ff;
14
+ position: relative;
15
+ align-items: center; /* Center content vertically */
16
+ justify-content: center; /* Center content horizontally */
17
+ background: url(/assets/py-detect-illustration.png) no-repeat center center fixed;
18
+ background-size: cover;
19
+ }
20
+
21
+ .left-section {
22
+ flex: 1;
23
+ display: flex;
24
+ flex-direction: column;
25
+ justify-content: center; /* Center vertically */
26
+ align-items: center; /* Center horizontally */
27
+ padding-left: 120px; /* Reduced from 240px to 120px */
28
+ padding-top: 0;
29
+ position: relative;
30
+ }
31
+
32
+ .topbar {
33
+ display: flex;
34
+ align-items: flex-start;
35
+ height: 80px;
36
+ padding-left: 0;
37
+ width: 100%;
38
+ position: absolute;
39
+ top: 0;
40
+ left: 0;
41
+ z-index: 30;
42
+ }
43
+
44
+ .logo {
45
+ width: 120px;
46
+ height: 120px;
47
+ margin-left: 0;
48
+ margin-top: 0;
49
+ position: absolute;
50
+ left: 0;
51
+ top: 0;
52
+ z-index: 40;
53
+ }
54
+
55
+ .logo-fixed {
56
+ position: fixed;
57
+ top: 0;
58
+ left: 0;
59
+ width: 120px;
60
+ height: 120px;
61
+ z-index: 100;
62
+ margin: 0;
63
+ }
64
+
65
+ /* Add gap between logo and browser border */
66
+ .logo-img {
67
+ width: 6vw;
68
+ height: 6vw;
69
+ border-radius: 50%;
70
+ box-shadow: 0 0 15px rgba(255, 255, 255, 0.8);
71
+ position: fixed;
72
+ top: 18px; /* Add gap from top */
73
+ left: 18px; /* Add gap from left */
74
+ z-index: 300;
75
+ margin: 0;
76
+ }
77
+
78
+ /* Move Py-Detect text further down by increasing top value of .logo-title-row */
79
+ .logo-title-row {
80
+ display: flex;
81
+ flex-direction: row;
82
+ align-items: center;
83
+ position: absolute;
84
+ top: 180px; /* Increased from 120px to move text further down */
85
+ left: 48px;
86
+ z-index: 300;
87
+ gap: 32px;
88
+ justify-content: flex-start;
89
+ width: 100%;
90
+ }
91
+
92
+ .py-detect-title {
93
+ position: fixed;
94
+ margin-left: 132px;
95
+ margin-top: -208px;
96
+ text-align: left;
97
+ font-size: 4vw;
98
+ color: #38bdf8;
99
+ font-family: 'Segoe UI', 'Arial', 'Roboto', sans-serif;
100
+ font-weight: 900;
101
+ letter-spacing: 6px;
102
+ background: none;
103
+ border: none;
104
+ box-shadow: none;
105
+ min-width: unset;
106
+ max-width: unset;
107
+ }
108
+
109
+ /* Py-Detect title: each letter with its own color */
110
+ .py-detect-title .py-letter.p {
111
+ color: #e3f6ff;
112
+ text-shadow: 0 0 6px #38bdf8;
113
+ }
114
+
115
+ .py-detect-title .py-letter.y {
116
+ color: #38bdf8;
117
+ text-shadow: 0 0 6px #38bdf8;
118
+ }
119
+
120
+ .py-detect-title .py-shape {
121
+ color: #e3f6ff;
122
+ background: #e3f6ff;
123
+ text-shadow: 0 0 6px #38bdf8;
124
+ box-shadow: 0 0 6px #38bdf8, 0 0 2px #fff;
125
+ border: 2px solid #23272b;
126
+ }
127
+
128
+ .py-detect-title .py-letter.d {
129
+ color: #e3f6ff;
130
+ text-shadow: 0 0 6px #38bdf8;
131
+ }
132
+
133
+ .py-detect-title .py-letter.e {
134
+ color: #38bdf8;
135
+ text-shadow: 0 0 6px #38bdf8;
136
+ }
137
+
138
+ .py-detect-title .py-letter.t {
139
+ color: #e3f6ff;
140
+ text-shadow: 0 0 6px #38bdf8;
141
+ }
142
+
143
+ .py-detect-title .py-letter.e2 {
144
+ color: #38bdf8;
145
+ text-shadow: 0 0 6px #38bdf8;
146
+ }
147
+
148
+ .py-detect-title .py-letter.c {
149
+ color: #e3f6ff;
150
+ text-shadow: 0 0 6px #38bdf8;
151
+ }
152
+
153
+ .py-detect-title .py-letter.t2 {
154
+ color: #38bdf8;
155
+ text-shadow: 0 0 6px #38bdf8;
156
+ }
157
+
158
+ .center-instructions {
159
+ position: absolute;
160
+ top: 50%;
161
+ left: 50%;
162
+ transform: translate(-50%, -50%);
163
+ z-index: 200;
164
+ width: 100%;
165
+ display: flex;
166
+ justify-content: left;
167
+ align-items: center;
168
+ }
169
+
170
+ .detection-info {
171
+ margin: 0 auto;
172
+ max-width: 500px;
173
+ background: rgba(30,41,59,0.85);
174
+ border-radius: 12px;
175
+ padding: 20px 28px;
176
+ box-shadow: 0 2px 16px #1e293b44, 0 0 12px #38bdf844;
177
+ font-size: 24px;
178
+ color: #e3f6ff;
179
+ font-weight: 500;
180
+ line-height: 1.4;
181
+ letter-spacing: 1px;
182
+ border-left: 6px solid #38bdf8;
183
+ border-top: 2px solid #1e293b;
184
+ backdrop-filter: blur(1px);
185
+ text-align: left;
186
+ }
187
+
188
+ .detection-info ul {
189
+ margin-top: 12px;
190
+ margin-bottom: 0;
191
+ font-size: 20px;
192
+ color: #bae6fd;
193
+ padding-left: 18px;
194
+ text-align: left; /* Align list to left */
195
+ }
196
+
197
+ .detection-info li {
198
+ margin-bottom: 6px;
199
+ padding-left: 2px;
200
+ font-weight: 400;
201
+ text-shadow: 0 0 4px #38bdf888;
202
+ text-align: left; /* Align each point to left */
203
+ }
204
+
205
+ .right-section {
206
+ flex: 1;
207
+ display: flex;
208
+ flex-direction: column;
209
+ justify-content: center;
210
+ align-items: flex-end;
211
+ padding-right: 0;
212
+ height: 100vh;
213
+ }
214
+
215
+ .sign-in-form {
216
+ background: rgba(30,41,59,0.92);
217
+ padding: 32px 48px 32px 64px; /* Reduced vertical and horizontal padding */
218
+ border-radius: 24px;
219
+ box-shadow: 0 8px 32px rgba(30,41,59,0.48), 0 0 24px #38bdf844;
220
+ display: flex;
221
+ flex-direction: column;
222
+ align-items: flex-start;
223
+ gap: 28px; /* Reduced gap between children */
224
+ min-width: 340px;
225
+ max-width: 480px;
226
+ border: 3px solid #38bdf8;
227
+ backdrop-filter: blur(2px);
228
+ transition: box-shadow 0.3s, border-color 0.3s;
229
+ height: 320px; /* Further reduced height */
230
+ min-height: 320px;
231
+ max-height: 340px;
232
+ }
233
+
234
+ .sign-in-form:hover {
235
+ box-shadow: 0 4px 32px #38bdf844, 0 0 16px #1e293b88;
236
+ border-color: #bae6fd;
237
+ }
238
+
239
+ .form-group {
240
+ display: flex;
241
+ flex-direction: column;
242
+ align-items: flex-start; /* Align label and input to left */
243
+ gap: 12px;
244
+ min-width: 180px;
245
+ width: 100%;
246
+ margin-left: -24px; /* Shift fields a bit to the left */
247
+ }
248
+
249
+ label {
250
+ color: #bae6fd;
251
+ font-size: 26px;
252
+ font-weight: 600;
253
+ margin-bottom: 4px;
254
+ letter-spacing: 1px;
255
+ text-shadow: 0 0 6px #38bdf888;
256
+ }
257
+
258
+ .input-field {
259
+ padding: 20px;
260
+ border-radius: 12px;
261
+ border: 3px solid #38bdf8;
262
+ background: rgba(30,41,59,0.98);
263
+ color: #e3f6ff;
264
+ font-size: 28px;
265
+ width: 100%;
266
+ box-shadow: 0 2px 12px #38bdf844;
267
+ transition: border-color 0.3s;
268
+ text-align: left; /* Align text to left */
269
+ }
270
+
271
+ .input-field:focus {
272
+ border-color: #bae6fd;
273
+ outline: none;
274
+ }
275
+
276
+ .input-field::placeholder {
277
+ color: #bae6fd;
278
+ font-size: 24px;
279
+ opacity: 0.8;
280
+ }
281
+
282
+ .sign-in-button {
283
+ background: linear-gradient(90deg, #38bdf8 0%, #23272b 100%);
284
+ color: #e3f6ff;
285
+ padding: 22px 0;
286
+ font-size: 32px;
287
+ border: none;
288
+ border-radius: 14px;
289
+ cursor: pointer;
290
+ transition: background 0.3s ease, box-shadow 0.3s ease;
291
+ width: 100%;
292
+ margin-top: 0;
293
+ font-weight: bold;
294
+ letter-spacing: 1px;
295
+ box-shadow: 0 2px 16px #38bdf888;
296
+ text-shadow: 0 0 6px #23272b;
297
+ align-self: stretch;
298
+ min-width: 180px;
299
+ }
300
+
301
+ .sign-in-button:hover {
302
+ background: linear-gradient(90deg, #23272b 0%, #38bdf8 100%);
303
+ color: #bae6fd;
304
+ box-shadow: 0 2px 24px #bae6fd88;
305
+ }
306
+
307
+ /* Buttons that open the popup */
308
+ .auth-links {
309
+ display: flex;
310
+ gap: 16px;
311
+ margin-top: -12px;
312
+ flex-wrap: wrap;
313
+ }
314
+
315
+ .link-btn {
316
+ background: transparent;
317
+ border: 1px dashed #38bdf8;
318
+ color: #bae6fd;
319
+ padding: 10px 16px;
320
+ border-radius: 10px;
321
+ cursor: pointer;
322
+ }
323
+
324
+ .link-btn:hover {
325
+ border-style: solid;
326
+ color: #e3f6ff;
327
+ }
328
+
329
+ .social-bar {
330
+ position: absolute;
331
+ top: 32px;
332
+ right: 60px;
333
+ display: flex;
334
+ gap: 24px;
335
+ z-index: 30;
336
+ }
337
+
338
+ .social-icon img {
339
+ width: 38px;
340
+ height: 38px;
341
+ filter: drop-shadow(0 0 6px #38bdf8);
342
+ transition: transform 0.2s;
343
+ }
344
+
345
+ .social-icon:hover img {
346
+ transform: scale(1.15);
347
+ filter: drop-shadow(0 0 12px #bae6fd);
348
+ }
349
+
350
+ .footer-link {
351
+ position: fixed;
352
+ left: 50%;
353
+ bottom: 24px;
354
+ transform: translateX(-50%);
355
+ font-size: 18px;
356
+ color: #bae6fd;
357
+ background: rgba(30,41,59,0.85);
358
+ border-radius: 8px;
359
+ padding: 10px 22px 10px 22px;
360
+ box-shadow: 0 2px 8px #1e293b44;
361
+ z-index: 40;
362
+ text-align: center;
363
+ min-width: 320px;
364
+ }
365
+
366
+ .footer-info {
367
+ font-size: 16px;
368
+ color: #bae6fd;
369
+ margin-bottom: 2px;
370
+ font-weight: 400;
371
+ }
372
+
373
+ .footer-link-highlight {
374
+ color: #38bdf8;
375
+ font-weight: 600;
376
+ }
377
+
378
+ .footer-link a {
379
+ color: #38bdf8;
380
+ text-decoration: none;
381
+ font-size: 18px;
382
+ font-weight: 500;
383
+ letter-spacing: 1px;
384
+ display: block;
385
+ margin-bottom: 2px;
386
+ }
387
+
388
+ .footer-link a:hover {
389
+ color: #e3f6ff;
390
+ }
391
+
392
+ .footer-tech {
393
+ font-size: 15px;
394
+ color: #e3f6ff;
395
+ margin-top: 2px;
396
+ font-weight: 600;
397
+ letter-spacing: 1px;
398
+ }
399
+
400
+ .login-title {
401
+ display: block;
402
+ font-size: 40px;
403
+ font-weight: 800;
404
+ color: #38bdf8;
405
+ text-align: left;
406
+ margin-bottom: 0;
407
+ letter-spacing: 2px;
408
+ }
409
+
410
+ .back-btn {
411
+ margin-top: 32px;
412
+ padding: 14px 38px;
413
+ font-size: 1.3rem;
414
+ font-weight: 700;
415
+ border-radius: 12px;
416
+ background: linear-gradient(90deg, #38bdf8 0%, #23272b 100%);
417
+ color: #e3f6ff;
418
+ border: none;
419
+ box-shadow: 0 2px 16px #38bdf888;
420
+ cursor: pointer;
421
+ transition: background 0.3s, color 0.3s, box-shadow 0.3s;
422
+ }
423
+
424
+ .back-btn:hover {
425
+ background: linear-gradient(90deg, #23272b 0%, #38bdf8 100%);
426
+ color: #bae6fd;
427
+ box-shadow: 0 2px 24px #bae6fd88;
428
+ }
429
+
430
+ /* ===== Modal (popup) styles ===== */
431
+ .modal-backdrop {
432
+ position: fixed;
433
+ inset: 0;
434
+ background: rgba(0,0,0,.45);
435
+ z-index: 1000;
436
+ }
437
+
438
+ .modal {
439
+ position: fixed;
440
+ inset: 0;
441
+ display: grid;
442
+ place-items: center;
443
+ padding: 24px;
444
+ z-index: 1001;
445
+ background: #ffffff45;
446
+ }
447
+
448
+ .modal > app-sign-in,
449
+ .modal > app-sign-up {
450
+ max-width: 900px;
451
+ width: min(92vw, 900px);
452
+ }
453
+
454
+ .modal__close {
455
+ position: absolute;
456
+ top: 18px;
457
+ right: 18px;
458
+ width: 48px;
459
+ height: 48px;
460
+ border: none;
461
+ background: linear-gradient(135deg, #23272b 60%, #18181b 100%);
462
+ color: #fff;
463
+ border-radius: 50%;
464
+ cursor: pointer;
465
+ font-size: 2.2rem;
466
+ font-weight: bold;
467
+ display: flex;
468
+ align-items: center;
469
+ justify-content: center;
470
+ box-shadow: 0 4px 16px rgba(30,41,59,0.28);
471
+ z-index: 20;
472
+ transition: background 0.2s, color 0.2s, box-shadow 0.2s;
473
+ outline: none;
474
+ }
475
+
476
+ .modal__close:hover {
477
+ background: linear-gradient(135deg, #333 60%, #23272b 100%);
478
+ color: #38bdf8;
479
+ box-shadow: 0 8px 24px #38bdf888;
480
+ }
481
+
482
+ .signin-close {
483
+ position: absolute;
484
+ top: 189px;
485
+ right: 363px;
486
+ width: 48px;
487
+ height: 48px;
488
+ border: none;
489
+ background: linear-gradient(135deg, #23272b 60%, #18181b 100%);
490
+ color: #fff;
491
+ border-radius: 50%;
492
+ cursor: pointer;
493
+ font-size: 2.2rem;
494
+ font-weight: bold;
495
+ display: flex;
496
+ align-items: center;
497
+ justify-content: center;
498
+ box-shadow: 0 4px 16px rgba(30,41,59,0.28);
499
+ z-index: 20;
500
+ transition: background 0.2s, color 0.2s, box-shadow 0.2s;
501
+ outline: none;
502
+ }
503
+
504
+ .signin-close:hover {
505
+ background: linear-gradient(135deg, #333 60%, #23272b 100%);
506
+ color: #38bdf8;
507
+ box-shadow: 0 8px 24px #38bdf888;
508
+ }
509
+
510
+ .signup-close {
511
+ position: absolute;
512
+ top: 110px;
513
+ right: 453px;
514
+ width: 48px;
515
+ height: 48px;
516
+ border: none;
517
+ background: linear-gradient(135deg, #23272b 60%, #18181b 100%);
518
+ color: #fff;
519
+ border-radius: 50%;
520
+ cursor: pointer;
521
+ font-size: 2.2rem;
522
+ font-weight: bold;
523
+ display: flex;
524
+ align-items: center;
525
+ justify-content: center;
526
+ box-shadow: 0 4px 16px rgba(30,41,59,0.28);
527
+ z-index: 20;
528
+ transition: background 0.2s, color 0.2s, box-shadow 0.2s;
529
+ outline: none;
530
+ }
531
+
532
+ .signup-close:hover {
533
+ background: linear-gradient(135deg, #333 60%, #23272b 100%);
534
+ color: #38bdf8;
535
+ box-shadow: 0 8px 24px #38bdf888;
536
+ }
537
+
538
+ /* Keep app-sign-in/up wrapper */
539
+ .app-sign-in, .app-sign-up {
540
+ position: relative;
541
+ background: #fff;
542
+ border-radius: 14px;
543
+ box-shadow: 0 20px 60px rgba(0,0,0,.35);
544
+ }
545
+
546
+ .py-shape {
547
+ display: inline-block;
548
+ width: 18px;
549
+ height: 4px;
550
+ background: #38bdf8;
551
+ margin: 0 8px;
552
+ vertical-align: middle;
553
+ border-radius: 2px;
554
+ box-shadow: 0 0 6px #38bdf8, 0 0 2px #fff;
555
+ border: 2px solid #23272b;
556
+ }
557
+
558
+ .auth-topright {
559
+ position: fixed;
560
+ top: 32px;
561
+ right: 48px;
562
+ z-index: 200;
563
+ display: flex;
564
+ gap: 18px;
565
+ }
566
+
567
+ .auth-btn {
568
+ font-family: 'Montserrat', 'Poppins', 'Arial Black', Arial, sans-serif;
569
+ font-size: 1.1rem;
570
+ font-weight: 700;
571
+ letter-spacing: 2px;
572
+ background: linear-gradient(90deg, #38bdf8 0%, #23272b 100%);
573
+ color: #e3f6ff;
574
+ box-shadow: 0 2px 16px #38bdf888;
575
+ border: none;
576
+ border-radius: 12px;
577
+ padding: 0.7rem 2rem;
578
+ cursor: pointer;
579
+ transition: background 0.3s, color 0.3s, box-shadow 0.3s;
580
+ }
581
+
582
+ .auth-btn:hover {
583
+ background: linear-gradient(90deg, #23272b 0%, #38bdf8 100%);
584
+ color: #bae6fd;
585
+ box-shadow: 0 2px 24px #bae6fd88;
586
+ }
587
+
588
+ .py-detect-image {
589
+ width: 420px;
590
+ max-width: 100%;
591
+ height: auto;
592
+ border-radius: 24px;
593
+ box-shadow: 0 8px 32px #38bdf844, 0 0 24px #1e293b88;
594
+ border: 3px solid #38bdf8;
595
+ background: #23272b;
596
+ margin-top: 48px;
597
+ margin-bottom: 48px;
598
+ display: block;
599
+ }
600
+
601
+ /* Footer Styles */
602
+ .footer {
603
+ background: linear-gradient(90deg, #18181b 0%, #38bdf8 100%); /* Dual color: black to cyan */
604
+ color: #fff; /* All text white */
605
+ padding: 0; /* Remove extra padding for a slim footer */
606
+ text-align: center;
607
+ width: 100%;
608
+ position: fixed;
609
+ bottom: 0;
610
+ left: 0;
611
+ z-index: 200;
612
+ height: 46px; /* Further reduced height for a slim footer */
613
+ display: flex;
614
+ align-items: center;
615
+ justify-content: center;
616
+ }
617
+
618
+ .footer-content {
619
+ display: flex;
620
+ flex-direction: row;
621
+ justify-content: space-between;
622
+ align-items: center;
623
+ width: 100%;
624
+ max-width: 1200px;
625
+ margin: 0 auto;
626
+ padding: 0 32px;
627
+ gap: 64px;
628
+ }
629
+
630
+ .footer-left,
631
+ .footer-center,
632
+ .footer-right {
633
+ margin: 0;
634
+ min-width: 0;
635
+ flex: 1 1 1;
636
+ display: flex;
637
+ align-items: center;
638
+ color: #fff !important;
639
+ justify-content: center;
640
+ }
641
+
642
+ .footer-center,
643
+ .footer-right {
644
+ color: #fff !important;
645
+ }
646
+
647
+ .footer-center .footer-links {
648
+ display: flex;
649
+ flex-direction: row;
650
+ gap: 32px;
651
+ align-items: center;
652
+ margin: 0;
653
+ padding: 0;
654
+ flex-wrap: nowrap;
655
+ white-space: nowrap;
656
+ }
657
+
658
+ .footer-center .footer-links li {
659
+ display: flex;
660
+ align-items: center;
661
+ margin: 0;
662
+ }
663
+
664
+ .footer-right .social-media {
665
+ display: flex;
666
+ flex-direction: row;
667
+ gap: 18px;
668
+ align-items: center;
669
+ margin-left: 24px;
670
+ }
671
+
672
+ .footer-center .footer-links a {
673
+ color: #fff !important;
674
+ text-decoration: none !important;
675
+ }
676
+
677
+ .footer-center .footer-links a:hover {
678
+ color: #38bdf8 !important;
679
+ text-decoration: none !important;
680
+ }
681
+
682
+ .footer-right .social-media a,
683
+ .footer-right .social-media i {
684
+ color: #fff !important;
685
+ text-decoration: none !important;
686
+ }
687
+
688
+ @media screen and (max-width: 900px) {
689
+ .footer-content {
690
+ flex-direction: column;
691
+ gap: 16px;
692
+ padding: 8px 8px;
693
+ }
694
+
695
+ .footer-left,
696
+ .footer-center,
697
+ .footer-right {
698
+ justify-content: center;
699
+ width: 100%;
700
+ margin: 0;
701
+ }
702
+
703
+ .footer-center .footer-links {
704
+ flex-wrap: wrap;
705
+ justify-content: center;
706
+ gap: 12px;
707
+ white-space: normal;
708
+ }
709
+
710
+ .footer-right .social-media {
711
+ margin-left: 0;
712
+ justify-content: center;
713
+ }
714
+ }
715
+
716
+ /* Align detection info boxes to start from left, with space at right */
717
+ .detection-info-row {
718
+ display: flex;
719
+ flex-direction: row;
720
+ flex-wrap: nowrap;
721
+ gap: 24px;
722
+ justify-content: flex-start;
723
+ align-items: stretch;
724
+ margin: 0 0 0 48px; /* Add left margin, remove auto centering */
725
+ width: auto;
726
+ max-width: 1200px;
727
+ }
728
+
729
+ .detection-info-box {
730
+ background: rgba(30,41,59,0.92);
731
+ border-radius: 16px;
732
+ box-shadow: 0 2px 16px #38bdf844, 0 0 12px #1e293b88;
733
+ border-left: 6px solid #38bdf8;
734
+ border-top: 2px solid #1e293b;
735
+ padding: 18px 24px 16px 24px;
736
+ min-width: 220px;
737
+ max-width: 280px;
738
+ flex: 1 1 0;
739
+ font-family: 'Segoe UI', 'Arial', 'Roboto', sans-serif;
740
+ color: #e3f6ff;
741
+ font-size: 22px;
742
+ font-weight: 400;
743
+ line-height: 1.5;
744
+ letter-spacing: 0.5px;
745
+ position: relative;
746
+ margin-bottom: 0;
747
+ height: 100%;
748
+ display: flex;
749
+ flex-direction: column;
750
+ justify-content: flex-start;
751
+ opacity: 0;
752
+ transform: translateY(40px) scale(0.98);
753
+ animation: detectionBoxFadeIn 0.7s cubic-bezier(0.23, 1, 0.32, 1) forwards;
754
+ transition: transform 0.25s cubic-bezier(0.23, 1, 0.32, 1), box-shadow 0.25s;
755
+ }
756
+
757
+ .detection-info-box:hover {
758
+ transform: scale(1.06);
759
+ box-shadow: 0 8px 32px #38bdf888, 0 0 24px #1e293b88;
760
+ z-index: 10;
761
+ }
762
+
763
+ .detection-info-box:nth-child(2) {
764
+ animation-delay: 0.15s;
765
+ }
766
+
767
+ .detection-info-box:nth-child(3) {
768
+ animation-delay: 0.3s;
769
+ }
770
+
771
+ .detection-info-box:nth-child(4) {
772
+ animation-delay: 0.45s;
773
+ }
774
+
775
+ @keyframes detectionBoxFadeIn {
776
+ 0% {
777
+ opacity: 0;
778
+ transform: translateY(40px) scale(0.98);
779
+ }
780
+
781
+ 80% {
782
+ opacity: 1;
783
+ transform: translateY(-4px) scale(1.02);
784
+ }
785
+
786
+ 100% {
787
+ opacity: 1;
788
+ transform: translateY(0) scale(1);
789
+ }
790
+ }
791
+
792
+ @media screen and (max-width: 1200px) {
793
+ .detection-info-row {
794
+ flex-wrap: wrap;
795
+ justify-content: center;
796
+ }
797
+
798
+ .detection-info-box {
799
+ max-width: 98vw;
800
+ min-width: 0;
801
+ font-size: 15px;
802
+ padding: 14px 6vw;
803
+ }
804
+ }
805
+
806
+ @media screen and (max-width: 900px) {
807
+ .detection-info-row {
808
+ flex-direction: column;
809
+ gap: 18px;
810
+ align-items: center;
811
+ }
812
+
813
+ .detection-info-box {
814
+ max-width: 98vw;
815
+ min-width: 0;
816
+ font-size: 15px;
817
+ padding: 14px 6vw;
818
+ height: auto;
819
+ }
820
+ }
821
+
822
+ /* Footer social icon styles */
823
+ .footer-social-icon {
824
+ width: 32px;
825
+ height: 32px;
826
+ margin: 0 8px;
827
+ vertical-align: middle;
828
+ filter: drop-shadow(0 0 6px #38bdf8);
829
+ transition: transform 0.2s, filter 0.2s;
830
+ }
831
+
832
+ .footer-social-icon:hover {
833
+ transform: scale(1.15);
834
+ filter: drop-shadow(0 0 12px #bae6fd);
835
+ }
836
+
837
+ /* Style for Next button in footer */
838
+ .footer-next-btn {
839
+ margin-left: 24px;
840
+ padding: 12px 32px;
841
+ font-size: 1.1rem;
842
+ font-weight: 600;
843
+ border-radius: 10px;
844
+ background: linear-gradient(90deg, #38bdf8 0%, #23272b 100%);
845
+ color: #e3f6ff;
846
+ border: none;
847
+ box-shadow: 0 2px 12px #38bdf888;
848
+ cursor: pointer;
849
+ transition: background 0.3s, color 0.3s, box-shadow 0.3s;
850
+ }
851
+
852
+ .footer-next-btn:hover {
853
+ background: linear-gradient(90deg, #23272b 0%, #38bdf8 100%);
854
+ color: #bae6fd;
855
+ box-shadow: 0 2px 24px #bae6fd88;
856
+ }
857
+
858
+ .tour-btn {
859
+ background: linear-gradient(90deg, #38bdf8 0%, #712d74 100%);
860
+ color: #fff;
861
+ font-size: 1.2rem;
862
+ font-weight: 700;
863
+ border: none;
864
+ border-radius: 12px;
865
+ padding: 14px 38px;
866
+ box-shadow: 0 2px 16px #38bdf888;
867
+ cursor: pointer;
868
+ margin: 0 auto;
869
+ display: block;
870
+ transition: background 0.3s, color 0.3s, box-shadow 0.3s;
871
+ }
872
+
873
+ .tour-btn:hover {
874
+ background: linear-gradient(90deg, #712d74 0%, #38bdf8 100%);
875
+ color: #bae6fd;
876
+ box-shadow: 0 2px 24px #bae6fd88;
877
+ }
878
+
879
+ .tour-overlay {
880
+ position: fixed;
881
+ inset: 0;
882
+ background: rgba(24,24,27,0.92);
883
+ z-index: 2000;
884
+ display: flex;
885
+ align-items: center;
886
+ justify-content: center;
887
+ }
888
+
889
+ .tour-content {
890
+ background: #23272b;
891
+ color: #fff;
892
+ border-radius: 18px;
893
+ padding: 48px 36px 36px 36px;
894
+ box-shadow: 0 8px 32px #38bdf844, 0 0 24px #1e293b88;
895
+ max-width: 480px;
896
+ width: 90vw;
897
+ text-align: center;
898
+ position: relative;
899
+ }
900
+
901
+ .tour-close {
902
+ position: absolute;
903
+ top: 18px;
904
+ right: 18px;
905
+ width: 44px;
906
+ height: 44px;
907
+ border: none;
908
+ background: #18181b;
909
+ color: #fff;
910
+ border-radius: 50%;
911
+ cursor: pointer;
912
+ font-size: 2rem;
913
+ font-weight: bold;
914
+ display: flex;
915
+ align-items: center;
916
+ justify-content: center;
917
+ box-shadow: 0 2px 8px rgba(0,0,0,0.18);
918
+ z-index: 10;
919
+ transition: background 0.2s, color 0.2s;
920
+ }
921
+
922
+ .tour-close:hover {
923
+ background: #333;
924
+ color: #f134d0;
925
+ }
926
+
927
+ .tour-next {
928
+ margin-top: 24px;
929
+ background: linear-gradient(90deg, #38bdf8 0%, #712d74 100%);
930
+ color: #fff;
931
+ font-size: 1rem;
932
+ font-weight: 600;
933
+ border: none;
934
+ border-radius: 10px;
935
+ padding: 12px 32px;
936
+ box-shadow: 0 2px 12px #38bdf888;
937
+ cursor: pointer;
938
+ transition: background 0.3s, color 0.3s, box-shadow 0.3s;
939
+ }
940
+
941
+ .tour-next:hover {
942
+ background: linear-gradient(90deg, #712d74 0%, #38bdf8 100%);
943
+ color: #bae6fd;
944
+ box-shadow: 0 2px 24px #bae6fd88;
945
+ }
946
+
947
+ .info-link {
948
+ margin-left: 12px;
949
+ color: #38bdf8;
950
+ font-size: 0.95em;
951
+ cursor: pointer;
952
+ text-decoration: underline;
953
+ font-weight: 500;
954
+ transition: color 0.2s;
955
+ }
956
+
957
+ .info-link:hover {
958
+ color: #bae6fd;
959
+ }
960
+
961
+ .detection-info-box .detection-description,
962
+ .detection-info-box .detection-list {
963
+ margin-top: 10px;
964
+ }
965
+
966
+ .detection-icon {
967
+ font-size: 3vw;
968
+ }
969
+
970
+ .detection-info-box .detection-icon {
971
+ font-size: 3vw !important;
972
+ }
973
+
974
+ .welcome-title {
975
+ margin-left: 12px;
976
+ margin-bottom: 8px;
977
+ }
978
+
979
+ .main-title {
980
+ font-size: 2.2em;
981
+ font-weight: bold;
982
+ background: linear-gradient(90deg, #f134d0 0%, #38bdf8 100%);
983
+ -webkit-background-clip: text;
984
+ -webkit-text-fill-color: transparent;
985
+ background-clip: text;
986
+ text-fill-color: transparent;
987
+ letter-spacing: 2px;
988
+ }
989
+
990
+ .sub-title {
991
+ font-size: 1.2em;
992
+ font-weight: bold;
993
+ color: #fff;
994
+ }
995
+
996
+ .detection-description {
997
+ margin: 12px 0 18px 0;
998
+ color: #e3f6ff;
999
+ font-size: 1em;
1000
+ font-weight: 400;
1001
+ }
1002
+
1003
+ .read-more-btn {
1004
+ background: linear-gradient(90deg, #38bdf8 0%, #23272b 100%);
1005
+ color: #fff;
1006
+ font-size: 1em;
1007
+ font-weight: 600;
1008
+ border: none;
1009
+ border-radius: 22px;
1010
+ padding: 10px 32px;
1011
+ box-shadow: 0 2px 12px #38bdf888;
1012
+ cursor: pointer;
1013
+ transition: background 0.3s, color 0.3s, box-shadow 0.3s;
1014
+ margin-top: 8px;
1015
+ letter-spacing: 1px;
1016
+ }
1017
+
1018
+ .read-more-btn:hover {
1019
+ background: linear-gradient(90deg, #23272b 0%, #38bdf8 100%);
1020
+ color: #bae6fd;
1021
+ box-shadow: 0 2px 24px #bae6fd88;
1022
+ }
1023
+
1024
+ /* ===================== */
1025
+ /* Info Dialog Animations */
1026
+ /* ===================== */
1027
+ @keyframes fadeInBackdrop {
1028
+ from {
1029
+ opacity: 0;
1030
+ }
1031
+
1032
+ to {
1033
+ opacity: 1;
1034
+ }
1035
+ }
1036
+
1037
+ @keyframes zoomInModal {
1038
+ 0% {
1039
+ opacity: 0;
1040
+ transform: translateY(8px) scale(0.96);
1041
+ }
1042
+
1043
+ 100% {
1044
+ opacity: 1;
1045
+ transform: translateY(0) scale(1);
1046
+ }
1047
+ }
1048
+
1049
+ .modal-backdrop--fade {
1050
+ animation: fadeInBackdrop .25s ease-out both;
1051
+ }
1052
+
1053
+ .dialog-modal--zoom {
1054
+ animation: zoomInModal .28s cubic-bezier(0.23, 1, 0.32, 1) both;
1055
+ }
1056
+
1057
+ /* Dialog content card */
1058
+ .dialog-content {
1059
+ background: #23272b;
1060
+ color: #fff;
1061
+ border-radius: 16px;
1062
+ padding: 36px;
1063
+ max-width: 720px;
1064
+ width: 92vw;
1065
+ text-align: left;
1066
+ position: relative;
1067
+ box-shadow: 0 8px 32px #38bdf844, 0 0 24px #1e293b88;
1068
+ }
1069
+
1070
+ /* Titles inside dialog */
1071
+ .dialog-title {
1072
+ color: #38bdf8;
1073
+ margin: 0 0 16px 0;
1074
+ font-size: 1.6rem;
1075
+ font-weight: 800;
1076
+ letter-spacing: 1px;
1077
+ }
1078
+
1079
+ .dialog-content h3 {
1080
+ margin-top: 18px;
1081
+ margin-bottom: 8px;
1082
+ color: #38bdf8;
1083
+ font-size: 1.15rem;
1084
+ }
1085
+
1086
+ .dialog-content p,
1087
+ .dialog-content ul {
1088
+ font-size: 1rem;
1089
+ color: #bae6fd;
1090
+ margin-bottom: 12px;
1091
+ }
1092
+
1093
+ .dialog-content ul {
1094
+ padding-left: 18px;
1095
+ }
1096
+
1097
+ /* Close button at top-right */
1098
+ .dialog-close {
1099
+ position: absolute;
1100
+ top: 18px;
1101
+ right: 18px;
1102
+ width: 40px;
1103
+ height: 40px;
1104
+ border: none;
1105
+ border-radius: 50%;
1106
+ background: #18181b;
1107
+ color: #fff;
1108
+ font-size: 1.6rem;
1109
+ font-weight: bold;
1110
+ cursor: pointer;
1111
+ box-shadow: 0 2px 8px rgba(0,0,0,0.25);
1112
+ transition: background 0.2s, color 0.2s;
1113
+ }
1114
+
1115
+ .dialog-close:hover {
1116
+ background: #333;
1117
+ color: #38bdf8;
1118
+ }
src/app/homepage/homepage.component.html ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- Logo and Py-Detect Title (kept as your last version) -->
2
+ <div class="logo-title-row">
3
+ <img src="/assets/pykara-logo.png" alt="PyDetect Logo" class="logo-img" />
4
+ <div class="py-detect-title">
5
+ <span class="py-letter p">P</span>
6
+ <span class="py-letter y">Y</span>
7
+ <span class="py-shape"></span>
8
+ <span class="py-letter d">D</span>
9
+ <span class="py-letter e">E</span>
10
+ <span class="py-letter t">T</span>
11
+ <span class="py-letter e2">E</span>
12
+ <span class="py-letter c">C</span>
13
+ <span class="py-letter t2">T</span>
14
+ </div>
15
+ </div>
16
+
17
+ <div class="homepage-container">
18
+ <!-- Top-right: Sign-In/Sign-Up buttons (reverted; unchanged from your last version) -->
19
+ <div class="auth-topright">
20
+ <button class="auth-btn" (click)="openSignIn()">Sign In</button>
21
+ <button class="auth-btn" (click)="openSignUp()">Sign Up</button>
22
+ </div>
23
+
24
+ <!-- Left-side instruction/info (position unchanged) -->
25
+ <div class="left-instructions"
26
+ style="position: absolute; left: 0; width: 50vw; flex-direction: column; justify-content: center; align-items: flex-start; padding-left: 80px; z-index: 10;">
27
+ <div style="max-width: 520px;">
28
+ <h1 style="font-size:2.5em; font-weight:800; color:#38bdf8; margin-bottom:12px;">
29
+ Welcome to Py-Detect
30
+ </h1>
31
+ <div style="font-size: 1.2em; color: #e3f6ff; margin-bottom: 18px; width: 33vw; text-align: justify;">
32
+ {{ welcomeText }}
33
+ </div>
34
+ <!-- Know More as a styled button -->
35
+ <button class="know-more-btn" (click)="openInfoDialog()"
36
+ style="font-size:1.1em; font-weight:600; color:#fff;
37
+ background:linear-gradient(90deg,#38bdf8 0%,#23272b 100%);
38
+ border:none; border-radius:22px; padding:10px 32px;
39
+ box-shadow:0 2px 12px #38bdf888; cursor:pointer;
40
+ transition:background 0.3s, color 0.3s, box-shadow 0.3s;
41
+ margin-top:8px; letter-spacing:1px;">
42
+ Know more
43
+ </button>
44
+ </div>
45
+ </div>
46
+
47
+ <!-- Optional tour overlay (unchanged) -->
48
+ <div class="tour-overlay" *ngIf="showTourOverlay">
49
+ <div class="tour-content">
50
+ <button class="tour__close" (click)="showTourOverlay = false" aria-label="Close">×</button>
51
+ <h2>Welcome to Py-Detect!</h2>
52
+ <p>Discover what Py-Detect can do for you:</p>
53
+ <ul>
54
+ <li>🕵️ Intelligent lie detection platform</li>
55
+ <li>🔍 AI-powered analysis of responses</li>
56
+ <li>🎯 Designed for investigators, educators, HR, and more</li>
57
+ <li>💡 Fast, non-invasive, and data-driven insights</li>
58
+ </ul>
59
+ <button class="tour-next" (click)="showTourOverlay = false">Got it!</button>
60
+ </div>
61
+ </div>
62
+ </div>
63
+
64
+ <!-- Footer (unchanged) -->
65
+ <footer class="footer">
66
+ <div class="footer-content">
67
+ <div class="footer-left">
68
+ <p>© 2025 Pykara Technologies. All rights reserved.</p>
69
+ </div>
70
+ <div class="footer-center">
71
+ <ul class="footer-links">
72
+ <li><a href="/about">About Us</a></li>
73
+ <li><a href="/contact">Contact</a></li>
74
+ <li><a href="/privacy">Privacy Policy</a></li>
75
+ <li><a href="/terms">Terms & Conditions</a></li>
76
+ </ul>
77
+ </div>
78
+ <div class="footer-right">
79
+ <div class="social-media">
80
+ <a href="https://facebook.com" target="_blank" aria-label="Facebook"><i class="fa-brands fa-facebook-f"></i></a>
81
+ <a href="https://twitter.com" target="_blank" aria-label="Twitter"><i class="fa-brands fa-twitter"></i></a>
82
+ <a href="https://linkedin.com" target="_blank" aria-label="LinkedIn"><i class="fa-brands fa-linkedin-in"></i></a>
83
+ <a href="https://instagram.com" target="_blank" aria-label="Instagram"><i class="fa-brands fa-instagram"></i></a>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </footer>
88
+
89
+ <!-- ===== Auth Modals (unchanged) ===== -->
90
+ <div class="modal-backdrop" *ngIf="showSignIn || showSignUp" (click)="closeModal()"></div>
91
+ <div class="modal" *ngIf="showSignIn || showSignUp" role="dialog" aria-modal="true">
92
+ <ng-container *ngIf="showSignIn">
93
+ <button class="signin-close" (click)="closeModal()" aria-label="Close">×</button>
94
+ <app-sign-in (switchToSignUp)="openSignUp()"
95
+ (signInSuccess)="handleSignInSuccess()">
96
+ </app-sign-in>
97
+ </ng-container>
98
+ <ng-container *ngIf="showSignUp">
99
+ <button class="signup-close" (click)="closeModal()" aria-label="Close">×</button>
100
+ <app-sign-up (switchToSignIn)="openSignIn()">
101
+ </app-sign-up>
102
+ </ng-container>
103
+ </div>
104
+
105
+ <!-- ===== Info Dialog (unchanged) ===== -->
106
+ <div class="modal-backdrop modal-backdrop--fade" *ngIf="showInfoDialog" (click)="closeInfoDialog()"></div>
107
+ <div class="modal dialog-modal dialog-modal--zoom" *ngIf="showInfoDialog" role="dialog" aria-modal="true" aria-label="About Py-Detect">
108
+ <div class="dialog-content">
109
+ <!-- Close button at top-right (replaces Back) -->
110
+ <button class="dialog-close" (click)="closeInfoDialog()" aria-label="Close">×</button>
111
+
112
+ <h3>How It Works</h3>
113
+ <p>Py-Detect asks personalized, context-aware questions and analyzes verbal, facial, and behavioral signals using AI and computer vision. It then produces a concise report with an estimated lie probability.</p>
114
+
115
+ <h3>Who Can Use Py-Detect?</h3>
116
+ <p>Investigators, law enforcement teams, educators and school administrators, HR professionals, recruiters, and any organization that needs reliable behavioral insights.</p>
117
+
118
+ <h3>Why Py-Detect?</h3>
119
+ <p>It is fast, non-invasive, configurable for different scenarios, and supports decisions with clear, data-driven evidence.</p>
120
+ </div>
121
+ </div>
src/app/homepage/homepage.component.spec.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { HomepageComponent } from './homepage.component';
4
+
5
+ describe('HomepageComponent', () => {
6
+ let component: HomepageComponent;
7
+ let fixture: ComponentFixture<HomepageComponent>;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({
11
+ declarations: [HomepageComponent]
12
+ });
13
+ fixture = TestBed.createComponent(HomepageComponent);
14
+ component = fixture.componentInstance;
15
+ fixture.detectChanges();
16
+ });
17
+
18
+ it('should create', () => {
19
+ expect(component).toBeTruthy();
20
+ });
21
+ });
src/app/homepage/homepage.component.ts ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component, Renderer2, OnInit, OnDestroy } from '@angular/core';
2
+ import { Router } from '@angular/router';
3
+ import { CommonModule } from '@angular/common';
4
+ import { SignInComponent } from './sign-in/sign-in.component';
5
+ import { SignUpComponent } from './sign-up/sign-up.component';
6
+
7
+ @Component({
8
+ selector: 'app-homepage',
9
+ standalone: true,
10
+ templateUrl: './homepage.component.html',
11
+ styleUrls: ['./homepage.component.css'],
12
+ imports: [CommonModule, SignInComponent, SignUpComponent]
13
+ })
14
+ export class HomepageComponent implements OnInit, OnDestroy {
15
+ // Keep your existing auth modal flags (reverted)
16
+ showSignIn = false;
17
+ showSignUp = false;
18
+ showTourOverlay = false;
19
+
20
+ // (Optional legacy flags kept intact if used elsewhere)
21
+ selectedInfo: string | null = null;
22
+ showDetails = false;
23
+
24
+ // New: "Know more" dialog flag
25
+ showInfoDialog = false;
26
+
27
+ welcomeText = `Py-Detect is an intelligent lie detection platform designed to analyze human responses and uncover hidden truths. During each session, we also record and analyze body language using video to provide deeper behavioral insights. Whether you're evaluating a suspect, interviewing a student, or conducting sensitive assessments, Py-Detect uses advanced algorithms to ask targeted questions and deliver a truthfulness score—a percentage-based estimate of how likely the person is being honest.`;
28
+
29
+ constructor(private router: Router, private renderer: Renderer2) { }
30
+
31
+ ngOnInit() {
32
+ this.renderer.addClass(document.body, 'homepage-bg');
33
+ }
34
+
35
+ ngOnDestroy() {
36
+ this.renderer.removeClass(document.body, 'homepage-bg');
37
+ }
38
+
39
+ // === Auth modal open/close (same as your last version in the template) ===
40
+ openSignIn() { this.showSignIn = true; this.showSignUp = false; }
41
+ openSignUp() { this.showSignUp = true; this.showSignIn = false; }
42
+ closeModal() { this.showSignIn = this.showSignUp = false; }
43
+
44
+ // If your <app-sign-in> emits signInSuccess, keep the same handler
45
+ handleSignInSuccess() {
46
+ this.closeModal();
47
+ // keep your existing navigation if any; otherwise, do nothing
48
+ // this.router.navigate(['/infopage']);
49
+ }
50
+
51
+ // === Info dialog controls ===
52
+ openInfoDialog() { this.showInfoDialog = true; }
53
+ closeInfoDialog() { this.showInfoDialog = false; }
54
+
55
+ // (Optional) preserved helper
56
+ toggleInfo(info: string) {
57
+ this.selectedInfo = (this.selectedInfo === info) ? null : info;
58
+ }
59
+ }
src/app/homepage/sign-in/sign-in.component.css ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :host {
2
+ display: block
3
+ }
4
+
5
+
6
+ .auth-box {
7
+ width: 54vw;
8
+ display: grid;
9
+ grid-template-columns: 1fr;
10
+ background: #2b1b6b;
11
+ border-radius: 18px;
12
+ overflow: hidden;
13
+ box-shadow: 0 20px 60px 0 #18181b, 0 0 32px #38bdf888;
14
+ min-height: 420px;
15
+ max-height: 520px;
16
+ z-index: 1100;
17
+ animation: popupZoomIn .35s cubic-bezier(0.23, 1, 0.32, 1);
18
+ }
19
+
20
+ @keyframes popupZoomIn {
21
+ 0% { opacity: 0; transform: scale(0.96) translateY(16px); }
22
+ 100% { opacity: 1; transform: scale(1) translateY(0); }
23
+ }
24
+
25
+ .panel-right {
26
+ position: relative;
27
+ background: radial-gradient(120% 120% at 20% 50%,rgba(0,0,0,.25) 0%,rgba(0,0,0,0) 60%);
28
+ width: 100%;
29
+ height: 100%;
30
+ display: flex;
31
+ align-items: stretch;
32
+ justify-content: stretch;
33
+ }
34
+
35
+ .right-image {
36
+ width: 100%;
37
+ height: 100%;
38
+ display: flex;
39
+ align-items: stretch;
40
+ justify-content: stretch;
41
+ }
42
+
43
+ .right-image img {
44
+ width: 100%;
45
+ height: 100%;
46
+ object-fit: cover;
47
+ display: block;
48
+ }
49
+
50
+ .panel-left {
51
+ padding: clamp(22px,3vw,38px);
52
+ background: white;
53
+ color: black;
54
+ }
55
+
56
+ .brand-mark {
57
+ width: 4vw;
58
+ margin-bottom: 14px;
59
+ border: 2px solid #b1b1b17d;
60
+ box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
61
+ }
62
+
63
+ .title {
64
+ margin: 0 0 6px;
65
+ font-size: clamp(22px,3vw,26px);
66
+ font-weight: 700
67
+ }
68
+ .subtitle {
69
+ margin: 0 0 8px; /* Reduced space below Create Account text */
70
+ }
71
+
72
+ .form {
73
+ display: grid;
74
+ gap: 12px
75
+ }
76
+
77
+ .field {
78
+ display: grid;
79
+ gap: 6px
80
+ }
81
+
82
+ label {
83
+ font-weight: 600;
84
+ font-size: 13px;
85
+ }
86
+
87
+ input[type="email"], input[type="password"] {
88
+ color: black;
89
+ border: 1px solid rgb(0 0 0 / 57%);
90
+ border-radius: 10px;
91
+ padding: 11px 12px;
92
+ outline: none
93
+ }
94
+
95
+ input::placeholder {
96
+ color: #808080;
97
+ }
98
+
99
+ input:focus {
100
+ border-color: #a78bfa;
101
+ box-shadow: 0 0 0 3px rgba(167,139,250,.25)
102
+ }
103
+
104
+ .error {
105
+ color: red;
106
+ font-size: 12px
107
+ }
108
+
109
+ .btn {
110
+ width: 100%;
111
+ border-radius: 999px;
112
+ padding: 12px 18px;
113
+ cursor: pointer
114
+ }
115
+
116
+ .btn-primary {
117
+ background: #0b0f1a;
118
+ color: #fff;
119
+ border: none;
120
+ font-weight: 700
121
+ }
122
+
123
+ .btn[aria-busy="true"] {
124
+ opacity: .75;
125
+ cursor: progress
126
+ }
127
+
128
+ .footnote {
129
+ margin: 14px 0 0;
130
+ font-size: 13px;
131
+ }
132
+
133
+ .footnote a {
134
+ color: red;
135
+ text-decoration: underline
136
+ }
137
+
138
+ .admin-info {
139
+ margin: 10px 0 16px 0;
140
+ color: #f134d0;
141
+ font-size: 1em;
142
+ font-weight: 600;
143
+ text-align: center;
144
+ }
145
+
146
+ .admin-checkbox {
147
+ display: flex;
148
+ align-items: center;
149
+ gap: 8px;
150
+ margin: 10px 0 0 0;
151
+ }
152
+ .admin-checkbox input[type="checkbox"] {
153
+ accent-color: #2b1b6b;
154
+ width: 18px;
155
+ height: 18px;
156
+ }
157
+ .admin-checkbox label {
158
+ color: #2b1b6b;
159
+ font-size: 1em;
160
+ font-weight: 600;
161
+ cursor: pointer;
162
+ }
163
+
164
+
165
+ @keyframes fadeInBackdrop {
166
+ from { opacity: 0; }
167
+ to { opacity: 1; }
168
+ }
169
+
170
+ @media(min-width:900px) {
171
+ .auth-box {
172
+ grid-template-columns: 520px 1fr
173
+ }
174
+ }
175
+
176
+
177
+ .topTitle {
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 21px;
181
+ }
182
+
183
+ .topHeader {
184
+ font-size: 1vw;
185
+ }
186
+
187
+ .signin-close {
188
+ position: absolute;
189
+ top: 158px;
190
+ right: 450px;
191
+ width: 44px;
192
+ height: 3px;
193
+ border: none;
194
+ background: #18181b;
195
+ color: #fff;
196
+ border-radius: 50%;
197
+ cursor: pointer;
198
+ font-size: 2rem;
199
+ font-weight: bold;
200
+ display: flex;
201
+ align-items: center;
202
+ justify-content: center;
203
+ box-shadow: 0 2px 8px rgba(0,0,0,0.18);
204
+ z-index: 10;
205
+ transition: background 0.2s, color 0.2s;
206
+ }
207
+
208
+ .signin-close:hover {
209
+ background: #333;
210
+ color: #f134d0;
211
+ }
212
+
213
+ .signin-close:hover, .signup-close:hover {
214
+ background: linear-gradient(135deg, #333 60%, #23272b 100%);
215
+ color: #38bdf8;
216
+ box-shadow: 0 8px 24px #38bdf888;
217
+ }
218
+
219
+ .modal__close {
220
+ position: absolute;
221
+ top: 18px;
222
+ right: 18px;
223
+ width: 44px;
224
+ height: 44px;
225
+ border: none;
226
+ background: #18181b;
227
+ color: #fff;
228
+ border-radius: 50%;
229
+ cursor: pointer;
230
+ font-size: 2rem;
231
+ font-weight: bold;
232
+ display: flex;
233
+ align-items: center;
234
+ justify-content: center;
235
+ box-shadow: 0 2px 8px rgba(0,0,0,0.18);
236
+ z-index: 10;
237
+ transition: background 0.2s, color 0.2s;
238
+ }
239
+
240
+ .modal__close:hover {
241
+ background: #333;
242
+ color: #f134d0;
243
+ }
src/app/homepage/sign-in/sign-in.component.html ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <section class="auth-page">
2
+ <div class="signin-modal-backdrop"></div>
3
+ <div class="auth-box" role="dialog" aria-labelledby="siTitle">
4
+ <!-- Left (form) -->
5
+ <div class="panel-left">
6
+ <div class="topTitle">
7
+ <img class="brand-mark" src="/assets/pykara-logo.png" alt="Brand" />
8
+ <span class="topHeader"><b>Welcome back!</b></span>
9
+ </div>
10
+ <h2 id="siTitle" class="title">Log in</h2>
11
+
12
+ <form class="form" [formGroup]="form" (ngSubmit)="signIn()" novalidate>
13
+ <div class="field">
14
+ <label for="email">
15
+ Email
16
+ </label>
17
+ <input id="email" type="email" formControlName="email" placeholder="you@example.com" />
18
+ </div>
19
+
20
+ <div class="field">
21
+ <label for="password">
22
+ Password
23
+ </label>
24
+ <input id="password" type="password" formControlName="password" placeholder="••••••••" />
25
+ </div>
26
+
27
+ <button class="btn btn-primary" type="submit">
28
+ Sign in
29
+ </button>
30
+ </form>
31
+
32
+ <p class="footnote">
33
+ New here?
34
+ <a href="#" (click)="goToSignUp(); $event.preventDefault()">Create an account</a>
35
+ </p>
36
+ </div>
37
+
38
+ <!-- Right (image) -->
39
+ <div class="panel-right">
40
+ <div class="right-image">
41
+ <img src="/assets/signin.png" alt="Decorative" />
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </section>
src/app/homepage/sign-in/sign-in.component.spec.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { SignInComponent } from './sign-in.component';
4
+
5
+ describe('SignInComponent', () => {
6
+ let component: SignInComponent;
7
+ let fixture: ComponentFixture<SignInComponent>;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({
11
+ declarations: [SignInComponent]
12
+ });
13
+ fixture = TestBed.createComponent(SignInComponent);
14
+ component = fixture.componentInstance;
15
+ fixture.detectChanges();
16
+ });
17
+
18
+ it('should create', () => {
19
+ expect(component).toBeTruthy();
20
+ });
21
+ });
src/app/homepage/sign-in/sign-in.component.ts ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // sign-in.component.ts
2
+ import { Component, ChangeDetectionStrategy, Output, EventEmitter } from '@angular/core';
3
+ import { CommonModule } from '@angular/common';
4
+ import { ReactiveFormsModule, FormBuilder, Validators, FormGroup } from '@angular/forms';
5
+ import { Router, RouterLink } from '@angular/router';
6
+ import { SignInService } from './sign-in.service'; // Import SignInService
7
+
8
+ @Component({
9
+ selector: 'app-sign-in',
10
+ standalone: true,
11
+ imports: [CommonModule, ReactiveFormsModule, RouterLink],
12
+ templateUrl: './sign-in.component.html',
13
+ styleUrls: ['./sign-in.component.css'],
14
+ changeDetection: ChangeDetectionStrategy.OnPush
15
+ })
16
+ export class SignInComponent {
17
+ @Output() switchToSignUp = new EventEmitter<void>();
18
+ form: FormGroup;
19
+
20
+ constructor(private fb: FormBuilder, private router: Router, private signInService: SignInService) {
21
+ this.form = this.fb.group({
22
+ email: ['', [Validators.required, Validators.email]], // Added email validation
23
+ password: ['', [Validators.required]]
24
+ });
25
+ }
26
+
27
+ goToSignUp() {
28
+ this.switchToSignUp.emit();
29
+ }
30
+
31
+ signIn() {
32
+ if (this.form.invalid) {
33
+ console.log("Form is invalid:", this.form.errors);
34
+ return;
35
+ }
36
+
37
+ const payload = {
38
+ email: this.form.get('email')?.value,
39
+ password: this.form.get('password')?.value
40
+ };
41
+
42
+ this.signInService.signIn(payload).subscribe(
43
+ (response) => {
44
+ console.log('Login successful:', response);
45
+ // Navigate to case details or home page after login success
46
+ this.router.navigate(['/case-details']);
47
+ },
48
+ (error) => {
49
+ console.error('Login failed:', error);
50
+ alert('Invalid email or password!');
51
+ }
52
+ );
53
+
54
+ }
55
+ }
src/app/homepage/sign-in/sign-in.service.spec.ts ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { TestBed } from '@angular/core/testing';
2
+
3
+ import { SignInService } from './sign-in.service';
4
+
5
+ describe('SignInService', () => {
6
+ let service: SignInService;
7
+
8
+ beforeEach(() => {
9
+ TestBed.configureTestingModule({});
10
+ service = TestBed.inject(SignInService);
11
+ });
12
+
13
+ it('should be created', () => {
14
+ expect(service).toBeTruthy();
15
+ });
16
+ });
src/app/homepage/sign-in/sign-in.service.ts ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Injectable } from '@angular/core';
2
+ import { HttpClient } from '@angular/common/http';
3
+ import { Observable } from 'rxjs';
4
+
5
+ @Injectable({
6
+ providedIn: 'root'
7
+ })
8
+ export class SignInService {
9
+
10
+ constructor(private http: HttpClient) { }
11
+
12
+ signIn(payload: any): Observable<any> {
13
+ return this.http.post('http://127.0.0.1:5000/sign-in', payload);
14
+ }
15
+ }
src/app/homepage/sign-up/sign-up.component.css ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :host {
2
+ display: block
3
+ }
4
+
5
+
6
+
7
+ /* Two-column card */
8
+ .auth-box {
9
+ width: 49vw;
10
+ display: grid;
11
+ grid-template-columns: 1fr;
12
+ background: #2b1b6b; /* purple base under left panel */
13
+ border-radius: 14px;
14
+ overflow: hidden;
15
+ box-shadow: 0 20px 60px rgba(0,0,0,.35);
16
+ position: relative;
17
+ }
18
+
19
+ /* Right column (image) */
20
+ .panel-right {
21
+ position: relative;
22
+ background: radial-gradient(120% 120% at 20% 50%, rgba(0,0,0,.25) 0%, rgba(0,0,0,0) 60%);
23
+ }
24
+
25
+ .panel-right::before {
26
+ /* vertical fold shade between left and right */
27
+ content: "";
28
+ position: absolute;
29
+ inset: 0;
30
+ background: linear-gradient(90deg, rgba(0,0,0,.45) 0%, rgba(0,0,0,0) 26%);
31
+ pointer-events: none;
32
+ }
33
+
34
+ .right-image {
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ width: 23.9vw;
39
+ }
40
+
41
+ .right-image img {
42
+ width: 100%;
43
+ display: block
44
+ }
45
+
46
+ /* Left column (form) */
47
+ .panel-left {
48
+ padding: clamp(22px,3.5vw,36px);
49
+ background: white; /* brighter purple */
50
+ color: black;
51
+ }
52
+
53
+ .brand-mark {
54
+ width: 4vw;
55
+ margin-bottom: 14px;
56
+ border: 2px solid #b1b1b17d;
57
+ box-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px;
58
+ }
59
+
60
+ .title {
61
+ margin: 0 0 6px;
62
+ font-size: clamp(22px,3vw,26px);
63
+ font-weight: 700
64
+ }
65
+
66
+ .subtitle {
67
+ margin: 0 0 18px;
68
+ }
69
+
70
+ .form {
71
+ display: grid;
72
+ gap: 12px
73
+ }
74
+
75
+ .field {
76
+ display: grid;
77
+ gap: 6px
78
+ }
79
+
80
+ label {
81
+ font-weight: 600;
82
+ font-size: 13px;
83
+ }
84
+
85
+ /* Inputs: translucent with subtle border */
86
+ input[type="text"], input[type="password"] {
87
+ color: #000000;
88
+ border: 1px solid rgb(0 0 0 / 57%);
89
+ border-radius: 10px;
90
+ padding: 11px 12px;
91
+ outline: none;
92
+ }
93
+
94
+ input::placeholder {
95
+ color: #808080
96
+ }
97
+
98
+ input:focus {
99
+ border-color: #a78bfa;
100
+ box-shadow: 0 0 0 3px rgba(167,139,250,.25)
101
+ }
102
+
103
+ select.input-field {
104
+ color: #000000;
105
+ border: 1px solid rgb(0 0 0 / 57%);
106
+ border-radius: 10px;
107
+ padding: 11px 12px;
108
+ outline: none;
109
+ font-size: 16px;
110
+ width: 100%;
111
+ background: #fff;
112
+ box-sizing: border-box;
113
+ }
114
+
115
+ select.input-field:focus {
116
+ border-color: #a78bfa;
117
+ box-shadow: 0 0 0 3px rgba(167,139,250,.25);
118
+ }
119
+
120
+ .error {
121
+ color: red;
122
+ font-size: 12px
123
+ }
124
+
125
+ /* Buttons */
126
+ .btn {
127
+ width: 100%;
128
+ border-radius: 999px;
129
+ padding: 12px 18px;
130
+ cursor: pointer
131
+ }
132
+
133
+ .btn-primary {
134
+ background: #0b0f1a;
135
+ color: #fff;
136
+ border: none;
137
+ font-weight: 700
138
+ }
139
+
140
+ .btn-dark {
141
+ background: #111827;
142
+ color: #fff;
143
+ border: none;
144
+ margin-top: 8px
145
+ }
146
+
147
+ .btn[aria-busy="true"] {
148
+ opacity: .75;
149
+ cursor: progress
150
+ }
151
+
152
+ .footnote {
153
+ margin: 14px 0 0;
154
+ font-size: 13px;
155
+ }
156
+
157
+ .footnote a {
158
+ color: red;
159
+ text-decoration: underline
160
+ }
161
+
162
+ /* Layout: stack on small, split on >= 900px */
163
+ @media(min-width:900px) {
164
+ .auth-box {
165
+ grid-template-columns: 520px 1fr
166
+ }
167
+ }
168
+
169
+ .topTitle {
170
+ display: flex;
171
+ align-items: center;
172
+ gap: 21px;
173
+ }
174
+
175
+ .topHeader {
176
+ font-size: 1vw;
177
+ }
178
+
179
+ @keyframes fadeInBackdrop {
180
+ from {
181
+ opacity: 0;
182
+ }
183
+
184
+ to {
185
+ opacity: 1;
186
+ }
187
+ }
188
+
189
+
190
+ .signup-close {
191
+ position: absolute;
192
+ top: 18px;
193
+ right: 18px;
194
+ width: 48px;
195
+ height: 48px;
196
+ border: none;
197
+ background: linear-gradient(135deg, #23272b 60%, #18181b 100%);
198
+ color: #fff;
199
+ border-radius: 50%;
200
+ cursor: pointer;
201
+ font-size: 2.2rem;
202
+ font-weight: bold;
203
+ display: flex;
204
+ align-items: center;
205
+ justify-content: center;
206
+ box-shadow: 0 4px 16px rgba(30,41,59,0.28);
207
+ z-index: 20;
208
+ transition: background 0.2s, color 0.2s, box-shadow 0.2s;
209
+ outline: none;
210
+ }
211
+
212
+ .signup-close:hover {
213
+ background: linear-gradient(135deg, #333 60%, #23272b 100%);
214
+ color: #38bdf8;
215
+ box-shadow: 0 8px 24px #38bdf888;
216
+ }
217
+
218
+ .signin-close {
219
+ position: absolute;
220
+ top: 18px;
221
+ right: 18px;
222
+ width: 48px;
223
+ height: 48px;
224
+ border: none;
225
+ background: linear-gradient(135deg, #23272b 60%, #18181b 100%);
226
+ color: #fff;
227
+ border-radius: 50%;
228
+ cursor: pointer;
229
+ font-size: 2.2rem;
230
+ font-weight: bold;
231
+ display: flex;
232
+ align-items: center;
233
+ justify-content: center;
234
+ box-shadow: 0 4px 16px rgba(30,41,59,0.28);
235
+ z-index: 20;
236
+ transition: background 0.2s, color 0.2s, box-shadow 0.2s;
237
+ outline: none;
238
+ }
239
+
240
+ .signin-close:hover {
241
+ background: linear-gradient(135deg, #333 60%, #23272b 100%);
242
+ color: #38bdf8;
243
+ box-shadow: 0 8px 24px #38bdf888;
244
+ }
245
+
src/app/homepage/sign-up/sign-up.component.html ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <section class="auth-page">
2
+ <div class="auth-box" role="dialog" aria-labelledby="suTitle">
3
+ <!-- Left (form) -->
4
+ <div class="panel-left">
5
+ <div class="topTitle">
6
+ <img class="brand-mark" src="/assets/pykara-logo.png" alt="Brand" />
7
+ <p class="topHeader"><b>Let's get started!</b></p>
8
+ </div>
9
+ <h2 id="suTitle" class="title">Create an account</h2>
10
+
11
+ <form [formGroup]="form" (ngSubmit)="submit()" novalidate>
12
+
13
+ <button class="btn btn-primary" type="submit">
14
+ Sign up
15
+ </button>
16
+ <!-- Name -->
17
+ <div class="field">
18
+ <label for="name">
19
+ Name
20
+ <small *ngIf="controlHasError('name','required')" class="error">*</small>
21
+ </label>
22
+ <input id="name" type="text" placeholder="John"
23
+ formControlName="name" [attr.aria-invalid]="controlHasError('name')" class="input-field" />
24
+ <small *ngIf="controlHasError('name','minlength')" class="error">Enter at least 2 characters.</small>
25
+ </div>
26
+
27
+ <!-- Role dropdown (moved below Name, styled) -->
28
+ <div class="field">
29
+ <label for="role">
30
+ Role
31
+ <small *ngIf="controlHasError('role','required')" class="error">*</small>
32
+ </label>
33
+ <select id="role" formControlName="role"
34
+ [attr.aria-invalid]="controlHasError('role')"
35
+ class="input-field" required>
36
+ <option value="">-- Select Role --</option>
37
+ <option value="teachers">Teachers</option>
38
+ <option value="lawyers">Lawyers</option>
39
+ <option value="investigators">Investigators</option>
40
+ <option value="others">Others</option>
41
+ </select>
42
+ </div>
43
+
44
+ <!-- Email/Phone (contact) -->
45
+ <div class="field">
46
+ <label for="contact">
47
+ Email
48
+ <small *ngIf="controlHasError('contact','required')" class="error">*</small>
49
+ </label>
50
+ <input id="email" type="text" placeholder="hello@gmail.com" formControlName="email" [attr.aria-invalid]="controlHasError('email')" class="input-field" />
51
+
52
+ <!--<input id="contact" type="text" placeholder="hello@gmail.com"
53
+ formControlName="contact" [attr.aria-invalid]="controlHasError('contact')" class="input-field" />-->
54
+ <small *ngIf="controlHasError('contact','pattern')" class="error">Enter a valid email/phone.</small>
55
+ </div>
56
+
57
+ <!-- Password -->
58
+ <div class="field">
59
+ <label for="password">
60
+ Password
61
+ <small *ngIf="controlHasError('password','required')" class="error">*</small>
62
+ </label>
63
+ <input id="password" type="password" placeholder="••••••••"
64
+ formControlName="password" [attr.aria-invalid]="controlHasError('password')" />
65
+ <small *ngIf="controlHasError('password','minlength')" class="error">Use at least 6 characters.</small>
66
+ </div>
67
+
68
+ <!-- Confirm password -->
69
+ <div class="field">
70
+ <label for="confirmPassword">
71
+ Confirm password
72
+ <small *ngIf="controlHasError('confirmPassword','required')" class="error">*</small>
73
+ </label>
74
+ <input id="confirmPassword" type="password" placeholder="••••••••"
75
+ formControlName="confirmPassword" [attr.aria-invalid]="showPwdMismatch()" />
76
+ <small *ngIf="showPwdMismatch()" class="error">Passwords do not match.</small>
77
+ </div>
78
+
79
+ <!--<button class="btn btn-primary" type="submit" [disabled]="form.invalid || submitting()" [attr.aria-busy]="submitting()">
80
+ Sign up1
81
+ </button>-->
82
+
83
+ <p class="footnote">
84
+ Already have an account?
85
+ <!-- CHANGED: Use click handler instead of routerLink -->
86
+ <a href="#" (click)="goToLogin(); $event.preventDefault()">Log in</a>
87
+ </p>
88
+ </form>
89
+ </div>
90
+
91
+ <!-- Right (image) -->
92
+ <div class="panel-right">
93
+ <div class="right-image">
94
+ <img src="/assets/signup.png" alt="Decorative" />
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </section>
src/app/homepage/sign-up/sign-up.component.spec.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { SignUpComponent } from './sign-up.component';
4
+
5
+ describe('SignUpComponent', () => {
6
+ let component: SignUpComponent;
7
+ let fixture: ComponentFixture<SignUpComponent>;
8
+
9
+ beforeEach(() => {
10
+ TestBed.configureTestingModule({
11
+ declarations: [SignUpComponent]
12
+ });
13
+ fixture = TestBed.createComponent(SignUpComponent);
14
+ component = fixture.componentInstance;
15
+ fixture.detectChanges();
16
+ });
17
+
18
+ it('should create', () => {
19
+ expect(component).toBeTruthy();
20
+ });
21
+ });
src/app/homepage/sign-up/sign-up.component.ts ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Component, Output, EventEmitter } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormBuilder, FormGroup, ReactiveFormsModule, Validators, AbstractControl } from '@angular/forms';
4
+ import { Router, RouterLink } from '@angular/router';
5
+ import { SignUpService } from './sign-up.service'; // Import the SignUpService
6
+
7
+ @Component({
8
+ selector: 'app-sign-up',
9
+ standalone: true,
10
+ imports: [CommonModule, ReactiveFormsModule, RouterLink],
11
+ templateUrl: './sign-up.component.html',
12
+ styleUrls: ['./sign-up.component.css']
13
+ })
14
+ export class SignUpComponent {
15
+ form: FormGroup;
16
+ private isSubmitting = false;
17
+
18
+ @Output() switchToSignIn = new EventEmitter<void>();
19
+
20
+ constructor(private fb: FormBuilder, private router: Router, private signUpService: SignUpService) { // Inject SignUpService
21
+ this.form = this.fb.group({
22
+ name: ['', [Validators.required, Validators.minLength(2)]],
23
+ email: ['', [
24
+ Validators.required,
25
+ Validators.pattern(/(^[^@]+@[^@]+\.[^@]+$)|(^\+?\d[\d\-\s]{8,14}\d$)/)
26
+ ]],
27
+ //gender: ['', [Validators.required]],
28
+ password: ['', [Validators.required, Validators.minLength(6)]],
29
+ confirmPassword: ['', [Validators.required]],
30
+ role: ['', [Validators.required]],
31
+ }, { validators: [this.passwordsMatchValidator] });
32
+ }
33
+
34
+ control(path: string): AbstractControl | null { return this.form.get(path); }
35
+
36
+ controlHasError(path: string, error?: string): boolean {
37
+ const c = this.control(path);
38
+ if (!c) return false;
39
+ return error ? !!(c.touched && c.errors?.[error]) : !!(c.touched && c.invalid);
40
+ }
41
+
42
+ showPwdMismatch(): boolean {
43
+ const pw = this.control('password');
44
+ const cpw = this.control('confirmPassword');
45
+ const groupMismatch = this.form.errors?.['passwordMismatch'];
46
+ return !!(pw && cpw && (cpw.touched || pw.touched) && groupMismatch);
47
+ }
48
+
49
+ passwordsMatchValidator(group: AbstractControl) {
50
+ const pw = group.get('password')?.value;
51
+ const cpw = group.get('confirmPassword')?.value;
52
+ return pw && cpw && pw === cpw ? null : { passwordMismatch: true };
53
+ }
54
+
55
+
56
+
57
+ async submit() {
58
+ // Confirm button click
59
+ alert("Sign-Up button clicked!");
60
+ console.log("Sign-Up button clicked!");
61
+
62
+ // Mark all form controls as touched to trigger validation
63
+ this.form.markAllAsTouched();
64
+
65
+ // Check if the form is invalid
66
+ if (this.form.invalid) {
67
+ console.log("Form is invalid:");
68
+
69
+ // Log individual control states for debugging
70
+ Object.keys(this.form.controls).forEach(controlName => {
71
+ const control = this.form.get(controlName);
72
+ if (control?.invalid) {
73
+ console.log(`${controlName} is invalid. Errors:`, control?.errors);
74
+ }
75
+ });
76
+ return;
77
+ }
78
+
79
+ try {
80
+ // Prepare the payload to send to the backend
81
+ const payload = {
82
+ name: this.control('name')?.value,
83
+ email: this.control('email')?.value,
84
+ //gender: this.control('gender')?.value,
85
+ password: this.control('password')?.value,
86
+ role: this.control('role')?.value
87
+ };
88
+
89
+ console.log("Form data before submission:", this.form.value);
90
+ console.log("Payload to send:", payload);
91
+
92
+ // Make the HTTP request
93
+ await this.signUpService.signUp(payload).toPromise();
94
+ console.log("Sign-up request sent successfully!");
95
+
96
+ // On success, navigate to Sign-In page
97
+ this.router.navigateByUrl('/auth/signin');
98
+ } catch (error) {
99
+ console.error("Error occurred during sign-up:", error); // Log any errors from the API call
100
+ }
101
+ }
102
+
103
+
104
+
105
+
106
+
107
+ navigateHome() { this.router.navigateByUrl('/'); }
108
+
109
+ goToLogin() {
110
+ this.switchToSignIn.emit(); // ← Emit event instead of router navigation
111
+ }
112
+
113
+ tr(key: string): string {
114
+ const map: Record<string, string> = { title: 'Create your account', subtitle: 'Join to continue' };
115
+ return map[key] || '';
116
+ }
117
+ }