Oviya
commited on
Commit
·
463dbc3
1
Parent(s):
bc7457a
all update
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .editorconfig +16 -0
- .gitignore +44 -0
- .vscode/extensions.json +4 -0
- .vscode/launch.json +19 -0
- .vscode/tasks.json +42 -0
- CHANGELOG.md +16 -0
- Py-Detect.esproj +16 -0
- Py-Detect.esproj.user +8 -0
- Py-Detect.sln +27 -0
- Py-Detect.vbproj +84 -0
- angular.json +106 -0
- app.config +25 -0
- karma.conf.js +44 -0
- obj/Debug/AngularProject1.esproj.CoreCompileInputs.cache +0 -0
- obj/Debug/AngularProject1.esproj.FileListAbsolute.txt +4 -0
- obj/Debug/Py-Detect.esproj.CoreCompileInputs.cache +0 -0
- obj/Debug/Py-Detect.esproj.FileListAbsolute.txt +1 -0
- obj/Debug/package.g.props +39 -0
- obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache +0 -0
- obj/x86/Debug/Py-Detect.vbproj.AssemblyReference.cache +0 -0
- obj/x86/Release/Py-Detect.vbproj.AssemblyReference.cache +0 -0
- package-lock.json +0 -0
- package.json +42 -0
- postcss.config.js +6 -0
- postcss.config.js.txt +0 -0
- src/app/app-routing.module.ts +37 -0
- src/app/app.component.css +0 -0
- src/app/app.component.html +5 -0
- src/app/app.component.spec.ts +27 -0
- src/app/app.component.ts +10 -0
- src/app/app.module.ts +38 -0
- src/app/case-details-page/case-details-page.component.css +330 -0
- src/app/case-details-page/case-details-page.component.html +107 -0
- src/app/case-details-page/case-details-page.component.spec.ts +21 -0
- src/app/case-details-page/case-details-page.component.ts +40 -0
- src/app/case-store.service.ts +104 -0
- src/app/homepage/homepage.component.css +1118 -0
- src/app/homepage/homepage.component.html +121 -0
- src/app/homepage/homepage.component.spec.ts +21 -0
- src/app/homepage/homepage.component.ts +59 -0
- src/app/homepage/sign-in/sign-in.component.css +243 -0
- src/app/homepage/sign-in/sign-in.component.html +45 -0
- src/app/homepage/sign-in/sign-in.component.spec.ts +21 -0
- src/app/homepage/sign-in/sign-in.component.ts +55 -0
- src/app/homepage/sign-in/sign-in.service.spec.ts +16 -0
- src/app/homepage/sign-in/sign-in.service.ts +15 -0
- src/app/homepage/sign-up/sign-up.component.css +245 -0
- src/app/homepage/sign-up/sign-up.component.html +98 -0
- src/app/homepage/sign-up/sign-up.component.spec.ts +21 -0
- 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 & 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 |
+
}
|