wip
This commit is contained in:
parent
7a2c1f03fa
commit
9853c6128c
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
backend/package-lock.json
|
||||||
15
.zed/debug.json
Normal file
15
.zed/debug.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"label": "PHP: Listen to Xdebug",
|
||||||
|
"adapter": "Xdebug",
|
||||||
|
"request": "launch",
|
||||||
|
"port": 9003,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "PHP: Debug this test",
|
||||||
|
"adapter": "Xdebug",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "vendor/bin/phpunit",
|
||||||
|
"args": ["--filter", "$ZED_SYMBOL"],
|
||||||
|
},
|
||||||
|
]
|
||||||
@ -4,7 +4,7 @@ APP_KEY=
|
|||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_URL=http://localhost
|
APP_URL=http://localhost
|
||||||
FRONTEND_URL="http://localhost:4200,http://127.0.0.1:4200"
|
FRONTEND_URL="http://localhost:4200,http://127.0.0.1:4200"
|
||||||
SANCTUM_STATEFUL_DOMAINS="${FRONTEND_URL}"
|
SANCTUM_STATEFUL_DOMAINS=localhost:4200,127.0.0.1:4200
|
||||||
|
|
||||||
APP_LOCALE=en
|
APP_LOCALE=en
|
||||||
APP_FALLBACK_LOCALE=en
|
APP_FALLBACK_LOCALE=en
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
#!/usr/bin/env php
|
#!/usr/bin/env php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Illuminate\Foundation\Application;
|
use Illuminate\Foundation\Application;
|
||||||
use Symfony\Component\Console\Input\ArgvInput;
|
use Symfony\Component\Console\Input\ArgvInput;
|
||||||
|
|
||||||
define('LARAVEL_START', microtime(true));
|
define("LARAVEL_START", microtime(true));
|
||||||
|
|
||||||
// Register the Composer autoloader...
|
// Register the Composer autoloader...
|
||||||
require __DIR__.'/vendor/autoload.php';
|
require __DIR__ . "/vendor/autoload.php";
|
||||||
|
|
||||||
// Bootstrap Laravel and handle the command...
|
// Bootstrap Laravel and handle the command...
|
||||||
/** @var Application $app */
|
/** @var Application $app */
|
||||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
$app = require_once __DIR__ . "/bootstrap/app.php";
|
||||||
|
|
||||||
$status = $app->handleCommand(new ArgvInput);
|
$status = $app->handleCommand(new ArgvInput());
|
||||||
|
|
||||||
exit($status);
|
exit($status);
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.23",
|
"fakerphp/faker": "^1.23",
|
||||||
|
"larastan/larastan": "^3.0",
|
||||||
"laravel/pail": "^1.2.5",
|
"laravel/pail": "^1.2.5",
|
||||||
"mockery/mockery": "^1.6",
|
"mockery/mockery": "^1.6",
|
||||||
"nunomaduro/collision": "^8.6",
|
"nunomaduro/collision": "^8.6",
|
||||||
|
|||||||
186
backend/composer.lock
generated
186
backend/composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "ce000f2979a5d252cefadab87899a5fc",
|
"content-hash": "3edf1e72a6c813ae77e28811f4fa4c1e",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "aws/aws-crt-php",
|
"name": "aws/aws-crt-php",
|
||||||
@ -6982,6 +6982,47 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-04-30T06:54:44+00:00"
|
"time": "2025-04-30T06:54:44+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "iamcal/sql-parser",
|
||||||
|
"version": "v0.7",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/iamcal/SQLParser.git",
|
||||||
|
"reference": "610392f38de49a44dab08dc1659960a29874c4b8"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/iamcal/SQLParser/zipball/610392f38de49a44dab08dc1659960a29874c4b8",
|
||||||
|
"reference": "610392f38de49a44dab08dc1659960a29874c4b8",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"php-coveralls/php-coveralls": "^1.0",
|
||||||
|
"phpunit/phpunit": "^5|^6|^7|^8|^9"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"iamcal\\": "src"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Cal Henderson",
|
||||||
|
"email": "cal@iamcal.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "MySQL schema parser",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/iamcal/SQLParser/issues",
|
||||||
|
"source": "https://github.com/iamcal/SQLParser/tree/v0.7"
|
||||||
|
},
|
||||||
|
"time": "2026-01-28T22:20:33+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "jean85/pretty-package-versions",
|
"name": "jean85/pretty-package-versions",
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
@ -7042,6 +7083,96 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-03-19T14:43:43+00:00"
|
"time": "2025-03-19T14:43:43+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "larastan/larastan",
|
||||||
|
"version": "v3.9.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/larastan/larastan.git",
|
||||||
|
"reference": "9ad17e83e96b63536cb6ac39c3d40d29ff9cf636"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/larastan/larastan/zipball/9ad17e83e96b63536cb6ac39c3d40d29ff9cf636",
|
||||||
|
"reference": "9ad17e83e96b63536cb6ac39c3d40d29ff9cf636",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-json": "*",
|
||||||
|
"iamcal/sql-parser": "^0.7.0",
|
||||||
|
"illuminate/console": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/container": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/contracts": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/database": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/http": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/pipeline": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"illuminate/support": "^11.44.2 || ^12.4.1 || ^13",
|
||||||
|
"php": "^8.2",
|
||||||
|
"phpstan/phpstan": "^2.1.44"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/coding-standard": "^13",
|
||||||
|
"laravel/framework": "^11.44.2 || ^12.7.2 || ^13",
|
||||||
|
"mockery/mockery": "^1.6.12",
|
||||||
|
"nikic/php-parser": "^5.4",
|
||||||
|
"orchestra/canvas": "^v9.2.2 || ^10.0.1 || ^11",
|
||||||
|
"orchestra/testbench-core": "^9.12.0 || ^10.1 || ^11",
|
||||||
|
"phpstan/phpstan-deprecation-rules": "^2.0.1",
|
||||||
|
"phpunit/phpunit": "^10.5.35 || ^11.5.15 || ^12.5.8"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"orchestra/testbench": "Using Larastan for analysing a package needs Testbench",
|
||||||
|
"phpmyadmin/sql-parser": "Install to enable Larastan's optional phpMyAdmin-based SQL parser automatically"
|
||||||
|
},
|
||||||
|
"type": "phpstan-extension",
|
||||||
|
"extra": {
|
||||||
|
"phpstan": {
|
||||||
|
"includes": [
|
||||||
|
"extension.neon"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"branch-alias": {
|
||||||
|
"dev-master": "3.0-dev"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Larastan\\Larastan\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Can Vural",
|
||||||
|
"email": "can9119@gmail.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel",
|
||||||
|
"keywords": [
|
||||||
|
"PHPStan",
|
||||||
|
"code analyse",
|
||||||
|
"code analysis",
|
||||||
|
"larastan",
|
||||||
|
"laravel",
|
||||||
|
"package",
|
||||||
|
"php",
|
||||||
|
"static analysis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/larastan/larastan/issues",
|
||||||
|
"source": "https://github.com/larastan/larastan/tree/v3.9.6"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/canvural",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-04-16T10:02:43+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/pail",
|
"name": "laravel/pail",
|
||||||
"version": "v1.2.6",
|
"version": "v1.2.6",
|
||||||
@ -8165,6 +8296,59 @@
|
|||||||
},
|
},
|
||||||
"time": "2026-01-25T14:56:51+00:00"
|
"time": "2026-01-25T14:56:51+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "phpstan/phpstan",
|
||||||
|
"version": "2.1.54",
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/8be50c3992107dc837b17da4d140fbbdf9a5c5bd",
|
||||||
|
"reference": "8be50c3992107dc837b17da4d140fbbdf9a5c5bd",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.4|^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"phpstan/phpstan-shim": "*"
|
||||||
|
},
|
||||||
|
"bin": [
|
||||||
|
"phpstan",
|
||||||
|
"phpstan.phar"
|
||||||
|
],
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "PHPStan - PHP Static Analysis Tool",
|
||||||
|
"keywords": [
|
||||||
|
"dev",
|
||||||
|
"static analysis"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"docs": "https://phpstan.org/user-guide/getting-started",
|
||||||
|
"forum": "https://github.com/phpstan/phpstan/discussions",
|
||||||
|
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||||
|
"security": "https://github.com/phpstan/phpstan/security/policy",
|
||||||
|
"source": "https://github.com/phpstan/phpstan-src"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://github.com/ondrejmirtes",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://github.com/phpstan",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2026-04-29T13:31:09+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "12.5.6",
|
"version": "12.5.6",
|
||||||
|
|||||||
17
backend/phpstan.neon
Normal file
17
backend/phpstan.neon
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
includes:
|
||||||
|
- vendor/larastan/larastan/extension.neon
|
||||||
|
- vendor/nesbot/carbon/extension.neon
|
||||||
|
|
||||||
|
parameters:
|
||||||
|
|
||||||
|
paths:
|
||||||
|
- app/
|
||||||
|
|
||||||
|
# Level 10 is the highest level
|
||||||
|
level: 8
|
||||||
|
|
||||||
|
# ignoreErrors:
|
||||||
|
# - '#PHPDoc tag @var#'
|
||||||
|
#
|
||||||
|
# excludePaths:
|
||||||
|
# - ./*/*/FileToBeExcluded.php
|
||||||
@ -5,9 +5,9 @@
|
|||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::get('/me', function (Request $request) {
|
Route::get("/me", function (Request $request) {
|
||||||
return $request->user();
|
return $request->user();
|
||||||
})->middleware('auth:sanctum');
|
})->middleware("auth:sanctum");
|
||||||
|
|
||||||
Route::apiResource('chats', ChatController::class);
|
Route::apiResource("chats", ChatController::class);
|
||||||
Route::apiResource('chats.messages', ChatMessageController::class);
|
Route::apiResource("chats.messages", ChatMessageController::class);
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { InitialsPipe } from './initials-pipe';
|
||||||
|
|
||||||
|
describe('InitialsPipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new InitialsPipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
17
frontend/src/app/core/layout/sidebar/initials-pipe.ts
Normal file
17
frontend/src/app/core/layout/sidebar/initials-pipe.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'initials',
|
||||||
|
})
|
||||||
|
export class InitialsPipe implements PipeTransform {
|
||||||
|
transform(value: string, separator: string = ' ', wordCount: number = 1): string {
|
||||||
|
if (!value) return '';
|
||||||
|
const parts = value.split(separator);
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < wordCount; i++) {
|
||||||
|
result += parts[i][0].toUpperCase();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -25,7 +25,7 @@
|
|||||||
<div class="flex items-center justify-between px-4 pt-5 pb-4 border-b border-[#1e2f4d]">
|
<div class="flex items-center justify-between px-4 pt-5 pb-4 border-b border-[#1e2f4d]">
|
||||||
<div class="flex items-center gap-2.5">
|
<div class="flex items-center gap-2.5">
|
||||||
<div
|
<div
|
||||||
class="w-8 h-8 rounded-full bg-linear-to-br from-[#00f2fe] to-[#4facfe] flex items-center justify-center font-semibold text-xl shadow-[0_0_20px_rgba(79,172,254,0.4)] animate-[pulse_2s_infinite]"
|
class="w-8 h-8 rounded-full bg-linear-to-br from-[#00f2fe] to-[#4facfe] flex items-center justify-center font-semibold shadow-[0_0_20px_rgba(79,172,254,0.4)] animate-[pulse_2s_infinite]"
|
||||||
>
|
>
|
||||||
AI
|
AI
|
||||||
</div>
|
</div>
|
||||||
@ -63,7 +63,7 @@
|
|||||||
<div class="px-3 pt-3 pb-2">
|
<div class="px-3 pt-3 pb-2">
|
||||||
<button
|
<button
|
||||||
(click)="newChat()"
|
(click)="newChat()"
|
||||||
class="w-full flex items-center justify-center gap-2 py-2.5 px-4 rounded-xl bg-[#2d5be3] hover:bg-[#3468f0] text-white text-sm font-medium transition-all duration-200 shadow-md hover:shadow-[#2d5be3]/30 hover:shadow-lg active:scale-[0.98]"
|
class="w-full flex items-center justify-center gap-2 py-2.5 px-4 rounded-xl bg-[#2d5be3] hover:bg-[#3468f0] text-white text-sm font-medium transition-all duration-200 shadow-md hover:shadow-[#2d5be3]/30 hover:shadow-lg"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@ -108,13 +108,15 @@
|
|||||||
<!-- Chat Items -->
|
<!-- Chat Items -->
|
||||||
<div class="space-y-0.5">
|
<div class="space-y-0.5">
|
||||||
@for (chat of chatStore.chats(); track chat.id) {
|
@for (chat of chatStore.chats(); track chat.id) {
|
||||||
<button (click)="selectChat(chat)" [class]="chatItemClasses(chat)">
|
<button (click)="selectChat(chat.id)" [class]="chatItemClasses(chat)">
|
||||||
<!-- Active indicator -->
|
<!-- Active indicator -->
|
||||||
|
@if(activeChatId() && activeChatId() === chat.id){
|
||||||
<div
|
<div
|
||||||
class="absolute left-0 top-1/2 -translate-y-1/2 w-0.5 h-6 bg-[#2d5be3] rounded-r-full"
|
class="absolute left-0 top-1/2 -translate-y-1/2 w-0.5 h-6 bg-[#2d5be3] rounded-r-full"
|
||||||
></div>
|
></div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="flex items-start gap-2.5 w-full min-w-0">
|
<div class="flex items-center gap-2.5 w-full min-w-0">
|
||||||
<!-- Icon -->
|
<!-- Icon -->
|
||||||
<div [class]="chatIconClasses(chat)">
|
<div [class]="chatIconClasses(chat)">
|
||||||
<svg
|
<svg
|
||||||
@ -135,7 +137,7 @@
|
|||||||
|
|
||||||
<!-- Text -->
|
<!-- Text -->
|
||||||
<div class="flex-1 min-w-0 text-left">
|
<div class="flex-1 min-w-0 text-left">
|
||||||
<p class="text-xs font-medium truncate text-white">{{ chat.attributes.title }}</p>
|
<p class="text-xs font-medium truncate">{{ chat.attributes.title }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Time -->
|
<!-- Time -->
|
||||||
@ -173,39 +175,67 @@
|
|||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<div class="border-t border-[#1e2f4d] px-3 py-3">
|
<div class="border-t border-[#1e2f4d] px-3 py-3">
|
||||||
<div class="flex items-center gap-2.5 transition-all duration-150 group">
|
<div class="flex items-center gap-2.5 transition-all duration-150">
|
||||||
@if(authStore.isLoading()) {
|
@if(authStore.isLoading()) {
|
||||||
<p>Loading...</p>
|
<p>Loading...</p>
|
||||||
} @else if(authStore.user() !== null){
|
} @else if(authStore.user() !== null){
|
||||||
<div
|
<div
|
||||||
class="w-7 h-7 rounded-lg bg-linear-to-br from-[#2d5be3] to-[#1a3a9e] flex items-center justify-center text-white text-xs font-bold shrink-0"
|
class="flex items-center hover:bg-black/9 px-4 py-2 rounded-xl w-full gap-2.5 transition-all duration-150"
|
||||||
>
|
>
|
||||||
K
|
<div
|
||||||
|
class="w-7 h-7 rounded-lg bg-linear-to-br from-[#2d5be3] to-[#1a3a9e] flex items-center justify-center text-white text-xs font-bold shrink-0"
|
||||||
|
>
|
||||||
|
{{authStore!.user()!.name | initials}}
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 min-w-0">
|
||||||
|
<p class="text-xs font-medium text-[#c8d8f0] truncate">{{authStore!.user()!.name}}</p>
|
||||||
|
<p class="text-[10px] text-[#3a5272] truncate">Free Plan</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative flex items-center">
|
||||||
|
<button
|
||||||
|
id="cog"
|
||||||
|
(click)="toggleCogMenu()"
|
||||||
|
class="bg-transparent border-none p-0 cursor-pointer flex items-center justify-center outline-none"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="w-3.5 h-3.5 text-[#3a5272] hover:text-[#5a80a8] transition-colors"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
@if(isCogMenuOpen()){
|
||||||
|
<div
|
||||||
|
id="cog-menu"
|
||||||
|
class="bg-black/9 backdrop-blur-sm text-xs text-gray-400 min-w-30 px-2 py-2 rounded-xl absolute right-0 bottom-4"
|
||||||
|
>
|
||||||
|
<ul class="">
|
||||||
|
<li>
|
||||||
|
<button class="w-full py-2 rounded-xl hover:bg-white/4 hover:text-red-400">
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 min-w-0">
|
|
||||||
<p class="text-xs font-medium text-[#c8d8f0] truncate">Kushal Saha</p>
|
|
||||||
<p class="text-[10px] text-[#3a5272] truncate">Free Plan</p>
|
|
||||||
</div>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="w-3.5 h-3.5 text-[#3a5272] group-hover:text-[#5a80a8] transition-colors"
|
|
||||||
fill="none"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
stroke="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
stroke-width="2"
|
|
||||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
} @else{
|
} @else{
|
||||||
<a
|
<a
|
||||||
routerLink="/user/login"
|
routerLink="/user/login"
|
||||||
|
|||||||
@ -3,11 +3,14 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { RouterModule } from '@angular/router';
|
import { RouterModule } from '@angular/router';
|
||||||
import { ChatStore } from '../../../chat/chat.store';
|
import { ChatStore } from '../../../chat/chat.store';
|
||||||
import { AuthStore } from '../../../auth/auth.store';
|
import { AuthStore } from '../../../auth/auth.store';
|
||||||
|
import { InitialsPipe } from './initials-pipe';
|
||||||
|
import { JsonApiResource } from '../../types/api';
|
||||||
|
import { Chat } from '../../../chat/chat.types';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sidebar',
|
selector: 'app-sidebar',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [CommonModule, RouterModule],
|
imports: [CommonModule, RouterModule, InitialsPipe],
|
||||||
templateUrl: 'sidebar.html',
|
templateUrl: 'sidebar.html',
|
||||||
styleUrl: 'sidebar.css',
|
styleUrl: 'sidebar.css',
|
||||||
})
|
})
|
||||||
@ -16,7 +19,9 @@ export class Sidebar implements OnInit {
|
|||||||
protected authStore = inject(AuthStore);
|
protected authStore = inject(AuthStore);
|
||||||
|
|
||||||
protected isOpen = signal(true);
|
protected isOpen = signal(true);
|
||||||
|
protected isCogMenuOpen = signal(false);
|
||||||
protected searchQuery = signal('');
|
protected searchQuery = signal('');
|
||||||
|
protected activeChatId = signal<string | null>(null);
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.chatStore.fetchChats();
|
this.chatStore.fetchChats();
|
||||||
@ -29,10 +34,11 @@ export class Sidebar implements OnInit {
|
|||||||
return this.isOpen() ? base + ' w-64' : base + ' w-0 border-none opacity-0';
|
return this.isOpen() ? base + ' w-64' : base + ' w-0 border-none opacity-0';
|
||||||
});
|
});
|
||||||
|
|
||||||
protected chatItemClasses(chat: any): string {
|
protected chatItemClasses(chat: JsonApiResource<Chat>): string {
|
||||||
const base =
|
const base =
|
||||||
'relative w-full flex items-center px-2 py-2 rounded-lg transition-all duration-150 cursor-pointer group';
|
'relative w-full flex items-center justify-center px-2 py-2 rounded-lg transition-all duration-150 cursor-pointer group';
|
||||||
return chat.isActive
|
|
||||||
|
return this.activeChatId() && chat.id === this.activeChatId()
|
||||||
? base + ' bg-[#13213d] text-white'
|
? base + ' bg-[#13213d] text-white'
|
||||||
: base + ' hover:bg-[#111a2e] text-[#6a8faf]';
|
: base + ' hover:bg-[#111a2e] text-[#6a8faf]';
|
||||||
}
|
}
|
||||||
@ -48,18 +54,16 @@ export class Sidebar implements OnInit {
|
|||||||
this.isOpen.update((v) => !v);
|
this.isOpen.update((v) => !v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected toggleCogMenu(): void {
|
||||||
|
this.isCogMenuOpen.update((v) => !v);
|
||||||
|
}
|
||||||
|
|
||||||
protected newChat(): void {
|
protected newChat(): void {
|
||||||
console.log('New chat triggered');
|
console.log('New chat triggered');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected selectChat(chat: any): void {
|
protected selectChat(chatId: string): void {
|
||||||
// this.activeChatId.set(chat.id);
|
this.activeChatId.set(chatId);
|
||||||
// this.sections.update((sections) =>
|
|
||||||
// sections.map((section) => ({
|
|
||||||
// ...section,
|
|
||||||
// chats: section.chats.map((c) => ({ ...c, isActive: c.id === chat.id })),
|
|
||||||
// })),
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected onSearch(event: Event): void {
|
protected onSearch(event: Event): void {
|
||||||
|
|||||||
@ -14,3 +14,8 @@ body {
|
|||||||
font-family: 'Outfit', sans-serif;
|
font-family: 'Outfit', sans-serif;
|
||||||
background: radial-gradient(circle at top right, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
background: radial-gradient(circle at top right, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
|
||||||
}
|
}
|
||||||
|
button:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
transition: scale;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user