Refactor imports to use new aliasing convention and update environment variable handling
- Changed imports from '$lib' to '#lib' for consistency across the codebase. - Updated environment variable imports to use the new structure from '$app/env/public' and '$app/env/private'. - Removed deprecated svelte.config.js and adjusted vite.config.js to accommodate new configurations. - Added stubs for environment variables in tests to ensure compatibility with the new import structure.
This commit is contained in:
parent
8eb68b5431
commit
f6e453098b
548 changed files with 3430 additions and 3375 deletions
|
|
@ -5,7 +5,7 @@ A free, self-hosted, multi-tenant CRM built with Django REST Framework, SvelteKi
|
|||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ Minimum versions, as declared in `pyproject.toml`. That file is the source of
|
|||
truth; the list here is a summary and `uv.lock` pins what actually gets
|
||||
installed.
|
||||
|
||||
- **Django 6.1.1+** - Web framework
|
||||
- **Django 6.1.2+** - Web framework
|
||||
- **Django REST Framework 3.17+** - API toolkit
|
||||
- **PostgreSQL** - Database, via **psycopg 3.2.10+** with the `pool` extra
|
||||
- **Celery 5.6+** - Async task queue
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||
|
||||
[project]
|
||||
name = "django-crm"
|
||||
version = "1.16.0"
|
||||
version = "1.17.0"
|
||||
# The one-line summary PyPI shows under the project title and in search results,
|
||||
# so it carries the terms people actually search for ("open source CRM",
|
||||
# "self-hosted CRM", "Django CRM") and names the modules rather than the
|
||||
|
|
@ -59,14 +59,14 @@ classifiers = [
|
|||
]
|
||||
|
||||
dependencies = [
|
||||
"django>=6.1.1",
|
||||
"django>=6.1.2",
|
||||
"celery>=5.6.3",
|
||||
"python-dotenv>=1.2.3",
|
||||
"python-dotenv>=1.2.4",
|
||||
"django-cors-headers>=4.9.0",
|
||||
"djangorestframework>=3.18.1",
|
||||
"djangorestframework>=3.18.3",
|
||||
"djangorestframework-simplejwt>=5.5.1",
|
||||
"drf-spectacular>=0.30.0",
|
||||
"django-ses>=4.8.0",
|
||||
"django-ses>=4.8.2",
|
||||
# psycopg 3, not psycopg2. The `pool` extra is required: Django refuses
|
||||
# pool options under psycopg2 (ImproperlyConfigured, "Database pooling
|
||||
# requires psycopg >= 3"), and DATABASES["default"]["OPTIONS"]["pool"] in
|
||||
|
|
@ -74,21 +74,21 @@ dependencies = [
|
|||
# directly, so the swap is confined to this line.
|
||||
"psycopg[binary,pool]>=3.3.6",
|
||||
"whitenoise>=6.12.0",
|
||||
"sentry-sdk>=2.70.0",
|
||||
"sentry-sdk>=2.72.0",
|
||||
"django-storages>=1.14.6",
|
||||
"django-crum>=0.7.9",
|
||||
"disposable-email-domains>=0.0.268",
|
||||
"disposable-email-domains>=0.0.280",
|
||||
"redis>=8.1.0",
|
||||
"pytz>=2026.4",
|
||||
"pytz>=2026.5",
|
||||
"requests>=2.34.2",
|
||||
"faker>=40.39.0",
|
||||
"faker>=40.43.0",
|
||||
"python-dateutil>=2.9.0.post0",
|
||||
"google-auth>=2.58.1",
|
||||
"google-auth>=2.62.0",
|
||||
# X.509 handling for inbound SNS webhook signature verification
|
||||
# (cases/inbound/sns.py). The API floor is 42, where the not_valid_*_utc
|
||||
# accessors landed; this pin sits higher only because floors here track the
|
||||
# versions in uv.lock.
|
||||
"cryptography>=50.0.1",
|
||||
"cryptography>=50.0.2",
|
||||
# Routing-rule `regex` conditions (cases/routing.py). RE2 matches in time
|
||||
# linear in the input, so an admin's pattern cannot backtrack for hours on
|
||||
# a ticket's tags or sender domain the way `re` can.
|
||||
|
|
@ -118,7 +118,7 @@ dev = [
|
|||
"pytest>=9.1.1",
|
||||
"pytest-django>=4.14.0",
|
||||
"pytest-cov>=7.1.0",
|
||||
"ruff>=0.16.9",
|
||||
"ruff>=0.17.0",
|
||||
]
|
||||
docs = [
|
||||
"mkdocs-material>=9.7.7",
|
||||
|
|
|
|||
1676
backend/uv.lock
1676
backend/uv.lock
File diff suppressed because it is too large
Load diff
|
|
@ -10,7 +10,7 @@ them.
|
|||
| Component | Version | Source |
|
||||
|---|---|---|
|
||||
| Python | 3.12 or later | `backend/pyproject.toml` sets `requires-python = ">=3.12"`; `backend/.python-version` pins `3.12` exactly. |
|
||||
| Django | 6.1.1 or later | `backend/pyproject.toml` dependency `django>=6.1.1`. |
|
||||
| Django | 6.1.2 or later | `backend/pyproject.toml` dependency `django>=6.1.2`. |
|
||||
| PostgreSQL | 16 | `docker-compose.yml`'s `db` service uses image `postgres:16-alpine`. This is also the only version this project's own CI ever runs against. See the note below. |
|
||||
| Redis | 7 | `docker-compose.yml`'s `redis` service uses image `redis:7-alpine`. |
|
||||
| Node.js | 24 | `.github/workflows/tests.yml`'s `frontend-checks` job sets up `node-version: '24'`. |
|
||||
|
|
|
|||
|
|
@ -7,21 +7,25 @@
|
|||
#
|
||||
# RUNTIME CONFIGURATION
|
||||
#
|
||||
# Every value this app reads from the environment comes through
|
||||
# `$env/dynamic/public`, which adapter-node resolves per process at startup, not
|
||||
# at build time. So one image serves any deployment and nothing below bakes a
|
||||
# hostname in:
|
||||
# Every value this app reads from the environment is declared in src/env.js and
|
||||
# read per process at startup, not at build time. So one image serves any
|
||||
# deployment and nothing below bakes a hostname in:
|
||||
#
|
||||
# PUBLIC_DJANGO_API_URL required. The Django API origin, no trailing /api.
|
||||
# ORIGIN required in production. See below.
|
||||
# PUBLIC_SENTRY_DSN optional. Unset disables Sentry (`enabled: !!dsn`).
|
||||
# PORT defaults to 3000. HOST already defaults to 0.0.0.0.
|
||||
# PROTOCOL_HEADER only for plain http. See below.
|
||||
#
|
||||
# ORIGIN is the one that fails quietly. adapter-node compares the Origin header
|
||||
# of every form POST against it, and with no ORIGIN set a proxied request is
|
||||
# answered `403 Cross-site POST form submissions are forbidden`. GETs still
|
||||
# work, so the app looks healthy while all 64 form actions reject. Set it to the
|
||||
# public URL users type, e.g. ORIGIN=https://crm.example.com.
|
||||
# The origin check is the one that fails quietly. SvelteKit compares the Origin
|
||||
# header of every form POST with the origin the server thinks it serves, which
|
||||
# adapter-node 6 builds from the request's Host header and the scheme `https`.
|
||||
# (The ORIGIN variable that used to override this was removed in SvelteKit 3,
|
||||
# so setting it does nothing.) Behind a TLS proxy that forwards Host, such as
|
||||
# nginx with `proxy_set_header Host $host`, that is already right. If users
|
||||
# reach the app over plain http, set PROTOCOL_HEADER=x-forwarded-proto and have
|
||||
# the proxy send that header. Otherwise every form POST is answered `403
|
||||
# Cross-site POST form submissions are forbidden` while GETs still work, so the
|
||||
# app looks healthy while all its form actions reject.
|
||||
|
||||
FROM node:24-slim AS base
|
||||
# Pinned so image builds are reproducible.
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ BottleCRM is a free, open-source Customer Relationship Management solution desig
|
|||
|
||||
- **Multi-Tenant Architecture**: Secure organization-based data isolation
|
||||
- **Role-Based Access Control**: Granular permissions for users and admins
|
||||
- **Modern Technology Stack**: Built with SvelteKit 2.x, Svelte 5.x, and PostgreSQL
|
||||
- **Modern Technology Stack**: Built with SvelteKit 3.x, Svelte 5.x, and PostgreSQL
|
||||
- **Mobile-First Design**: Responsive interface optimized for all devices
|
||||
|
||||
## 🚀 Core Features
|
||||
|
|
@ -57,7 +57,7 @@ BottleCRM is a free, open-source Customer Relationship Management solution desig
|
|||
|
||||
## 🖥️ Technology Stack
|
||||
|
||||
- **Frontend**: SvelteKit 2.x, Svelte 5.x, TailwindCSS 4.x
|
||||
- **Frontend**: SvelteKit 3.x, Svelte 5.x, TailwindCSS 4.x
|
||||
- **Backend**: Node.js with Prisma ORM
|
||||
- **Database**: PostgreSQL (recommended) with multi-tenant schema
|
||||
- **Authentication**: Session-based authentication with organization membership
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import { includeIgnoreFile } from '@eslint/compat';
|
|||
import svelte from 'eslint-plugin-svelte';
|
||||
import globals from 'globals';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import svelteConfig from './svelte.config.js';
|
||||
|
||||
const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url));
|
||||
|
||||
|
|
@ -19,10 +18,6 @@ export default [
|
|||
globals: { ...globals.browser, ...globals.node }
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.svelte', '**/*.svelte.js'],
|
||||
languageOptions: { parserOptions: { svelteConfig } }
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'no-unused-vars': ['warn', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }],
|
||||
|
|
|
|||
|
|
@ -1,22 +1,11 @@
|
|||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"extends": "$app/tsconfig",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"strictNullChecks": false,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||
"strictNullChecks": false
|
||||
},
|
||||
"include": ["src", "vite.config.js", "test"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,38 +19,42 @@
|
|||
"@eslint/compat": "^2.1.1",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@internationalized/date": "^3.12.4",
|
||||
"@lucide/svelte": "^1.48.0",
|
||||
"@sveltejs/adapter-node": "^5.5.7",
|
||||
"@sveltejs/kit": "^2.70.3",
|
||||
"@lucide/svelte": "^1.57.0",
|
||||
"@sveltejs/adapter-node": "^6.0.0",
|
||||
"@sveltejs/kit": "^3.0.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^7.3.1",
|
||||
"@tailwindcss/typography": "^0.5.20",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@types/node": "^26.6.2",
|
||||
"bits-ui": "^2.19.3",
|
||||
"eslint": "^10.11.0",
|
||||
"@types/node": "^26.6.5",
|
||||
"bits-ui": "^2.19.5",
|
||||
"eslint": "^10.12.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-svelte": "^3.23.0",
|
||||
"globals": "^17.12.0",
|
||||
"eslint-plugin-svelte": "^3.23.1",
|
||||
"globals": "^17.13.0",
|
||||
"mode-watcher": "^1.1.0",
|
||||
"prettier": "^3.9.9",
|
||||
"prettier": "^3.9.10",
|
||||
"prettier-plugin-svelte": "^4.1.1",
|
||||
"prettier-plugin-tailwindcss": "^0.8.1",
|
||||
"svelte": "^5.57.1",
|
||||
"svelte": "^5.57.2",
|
||||
"svelte-check": "^4.7.6",
|
||||
"svelte-sonner": "^1.2.1",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"typescript": "^6.0.3",
|
||||
"vite": "^8.3.1",
|
||||
"vitest": "^5.0.2"
|
||||
"vite": "^8.3.4",
|
||||
"vitest": "^5.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/sveltekit": "^11.0.0",
|
||||
"@sentry/sveltekit": "^11.7.0",
|
||||
"axios": "^1.20.0",
|
||||
"clsx": "^2.1.1",
|
||||
"libphonenumber-js": "^1.13.14",
|
||||
"svelte-dnd-action": "^0.9.79",
|
||||
"libphonenumber-js": "^1.13.15",
|
||||
"svelte-dnd-action": "^0.9.80",
|
||||
"tailwind-merge": "^3.7.0",
|
||||
"tailwind-variants": "^3.3.1",
|
||||
"tw-animate-css": "^1.4.0"
|
||||
},
|
||||
"imports": {
|
||||
"#lib": "./src/lib/index.js",
|
||||
"#lib/*": "./src/lib/*"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -7,9 +7,8 @@ overrides:
|
|||
# Only reachable through dev tooling (eslint, @sentry/vite-plugin), but the
|
||||
# transitive parents still ask for a vulnerable range, so force the patch.
|
||||
'brace-expansion@>=4.0.0 <5.0.9': '^5.0.9'
|
||||
# GHSA-pxg6-pf52-xh8x (low): cookie name/path/domain accept out-of-bounds
|
||||
# characters. Reachable at runtime, not just in dev tooling: @sveltejs/kit
|
||||
# parses and serialises the jwt_access / jwt_refresh / org cookies through
|
||||
# this package. Kit still asks for ^0.6.0, so force the patched line. Kit
|
||||
# only calls parse() and serialize(), both unchanged in 0.7.
|
||||
'cookie@<0.7.0': '^0.7.2'
|
||||
# GHSA-rj75-hqrm-r3gf (moderate): quadratic parsing of flat selectors.
|
||||
# Build-time only: @tailwindcss/typography 0.5.20 pins 6.0.10 exactly. 7.0's
|
||||
# one breaking change makes insertion during iteration safe; typography only
|
||||
# removes nodes from a copied array, and the built CSS is byte-identical.
|
||||
'postcss-selector-parser@<7.1.6': '^7.1.6'
|
||||
|
|
|
|||
33
frontend/src/env.js
Normal file
33
frontend/src/env.js
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { defineEnvVars } from '@sveltejs/kit/env';
|
||||
|
||||
/**
|
||||
* Every environment variable the app reads. SvelteKit 3 exposes only what is
|
||||
* declared here, through `$app/env/public` and `$app/env/private`.
|
||||
*
|
||||
* None is `static`, so each is read when the server starts rather than baked
|
||||
* in at build time: one Docker image serves any deployment. Each schema passes
|
||||
* the raw value through, so an unset variable is `undefined`, which is what
|
||||
* every caller already handles.
|
||||
*/
|
||||
/** @param {string | undefined} value */
|
||||
const optional = (value) => value;
|
||||
|
||||
export const variables = defineEnvVars({
|
||||
PUBLIC_DJANGO_API_URL: {
|
||||
public: true,
|
||||
schema: optional,
|
||||
description: 'The Django API origin, without a trailing /api.'
|
||||
},
|
||||
PUBLIC_SENTRY_DSN: {
|
||||
public: true,
|
||||
schema: optional,
|
||||
description: 'Sentry DSN. Unset disables Sentry.'
|
||||
},
|
||||
RELAY_SECRET: {
|
||||
schema: optional,
|
||||
description: "Shared with the API's RELAY_SECRET so it believes the relayed visitor address."
|
||||
},
|
||||
GOOGLE_CLIENT_ID: { schema: optional },
|
||||
GOOGLE_LOGIN_DOMAIN: { schema: optional },
|
||||
NODE_ENV: { schema: optional }
|
||||
});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { handleErrorWithSentry, replayIntegration } from '@sentry/sveltekit';
|
||||
import * as Sentry from '@sentry/sveltekit';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import * as env from '$app/env/public';
|
||||
|
||||
const dsn = env.PUBLIC_SENTRY_DSN || '';
|
||||
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import * as Sentry from '@sentry/sveltekit';
|
|||
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import axios from 'axios';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { isOrgAdmin } from '$lib/admin.js';
|
||||
import { describeError } from '$lib/server/log-safe.js';
|
||||
import { relayHeaders } from '$lib/server/relay.js';
|
||||
import * as env from '$app/env/public';
|
||||
import { isOrgAdmin } from '#lib/admin.js';
|
||||
import { describeError } from '#lib/server/log-safe.js';
|
||||
import { relayHeaders } from '#lib/server/relay.js';
|
||||
|
||||
const API_BASE_URL = `${env.PUBLIC_DJANGO_API_URL}/api`;
|
||||
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
|
|
@ -120,7 +120,7 @@ function refreshAccessToken(refreshToken, event) {
|
|||
* It deduplicates concurrent callers.
|
||||
*
|
||||
* The API writes an audit row for every refresh, so the visitor's signed
|
||||
* address goes along (`$lib/server/relay.js`); without it the row records
|
||||
* address goes along (`#lib/server/relay.js`); without it the row records
|
||||
* this server.
|
||||
*
|
||||
* @param {string} refreshToken - JWT refresh token
|
||||
|
|
@ -160,7 +160,7 @@ async function performTokenRefresh(refreshToken, event) {
|
|||
* @param {string} orgId - Organization UUID to switch to
|
||||
* @param {string | undefined} refreshToken - Refresh token being replaced, to be retired
|
||||
* @param {{ getClientAddress: () => string, request: Request }} event - For the visitor's address,
|
||||
* which the API's org-switch audit row records (`$lib/server/relay.js`)
|
||||
* which the API's org-switch audit row records (`#lib/server/relay.js`)
|
||||
* @returns {Promise<SwitchOrgResult|null>} New tokens and org data or null if failed
|
||||
*/
|
||||
async function switchOrg(accessToken, orgId, refreshToken, event) {
|
||||
|
|
@ -265,7 +265,7 @@ export const handle = sequence(Sentry.sentryHandle(), async function _handle({ e
|
|||
/** @type {any} */ (event.locals).profile = {
|
||||
org: event.locals.org,
|
||||
role: jwtPayload.role || 'USER',
|
||||
// The admin fact every admin gate reads (`$lib/admin.js`). Server
|
||||
// The admin fact every admin gate reads (`#lib/admin.js`). Server
|
||||
// derived and signed; `role` stays for display only.
|
||||
is_organization_admin: isOrgAdmin(jwtPayload)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* The shell's token refresh and org switch send the visitor's signed address
|
||||
* (`$lib/server/relay.js`): the API writes an audit row for each, and without
|
||||
* (`#lib/server/relay.js`): the API writes an audit row for each, and without
|
||||
* it the row records this server.
|
||||
*/
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
|
@ -20,7 +20,9 @@ vi.mock('@sveltejs/kit/hooks', () => ({
|
|||
vi.mock('axios', () => ({ default: { post: vi.fn(), get: vi.fn() } }));
|
||||
|
||||
const axios = (await import('axios')).default;
|
||||
const { env: privateEnv } = await import('$env/dynamic/private');
|
||||
// The stub that vitest.config.js aliases `$app/env/private` to, so this is the module the code
|
||||
// under test reads. Imported by path because only the stub has a setter.
|
||||
const { setRelaySecret } = await import('../test/stubs/app-env-private.js');
|
||||
const { handle } = await import('./hooks.server.js');
|
||||
|
||||
const SECRET = 'r'.repeat(48);
|
||||
|
|
@ -62,12 +64,12 @@ const callTo = (path) =>
|
|||
vi.mocked(axios.post).mock.calls.find(([url]) => String(url).endsWith(path));
|
||||
|
||||
beforeEach(() => {
|
||||
privateEnv.RELAY_SECRET = SECRET;
|
||||
setRelaySecret(SECRET);
|
||||
vi.mocked(axios.post).mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete privateEnv.RELAY_SECRET;
|
||||
setRelaySecret(undefined);
|
||||
});
|
||||
|
||||
describe('hooks.server.js relays the visitor', () => {
|
||||
|
|
@ -110,7 +112,7 @@ describe('hooks.server.js relays the visitor', () => {
|
|||
});
|
||||
|
||||
it('sends only the pre-1.12 X-Forwarded-For without a secret', async () => {
|
||||
delete privateEnv.RELAY_SECRET;
|
||||
setRelaySecret(undefined);
|
||||
vi.mocked(axios.post).mockResolvedValue({ data: {} });
|
||||
|
||||
await handle({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import * as Sentry from '@sentry/sveltekit';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import * as env from '$app/env/public';
|
||||
|
||||
const dsn = env.PUBLIC_SENTRY_DSN || '';
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* Used by SvelteKit server files during migration from Prisma to API.
|
||||
*/
|
||||
|
||||
import { env } from '$env/dynamic/public';
|
||||
import * as env from '$app/env/public';
|
||||
|
||||
const API_BASE_URL = `${env.PUBLIC_DJANGO_API_URL}/api`;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { resolve } from '$app/paths';
|
|||
* @module lib/api
|
||||
*/
|
||||
|
||||
import { env } from '$env/dynamic/public';
|
||||
import * as env from '$app/env/public';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
|
|
@ -177,7 +177,7 @@ async function performTokenRefresh() {
|
|||
// Refresh token expired or invalid
|
||||
clearAuthData();
|
||||
if (typeof window !== 'undefined') {
|
||||
goto(resolve('/login'));
|
||||
goto(resolve('login'));
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
|
|
@ -318,7 +318,7 @@ export const auth = {
|
|||
logout() {
|
||||
clearAuthData();
|
||||
if (typeof window !== 'undefined') {
|
||||
goto(resolve('/login'));
|
||||
goto(resolve('login'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script>
|
||||
import { resolve } from '$app/paths';
|
||||
import { asInternalPath } from '$lib/utils/paths.js';
|
||||
import { asInternalPath } from '#lib/utils/paths.js';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import { ArrowLeft, Home, Compass, Users, FileText, Ticket, BarChart3 } from '@lucide/svelte';
|
||||
|
||||
/**
|
||||
|
|
@ -46,7 +46,7 @@
|
|||
|
||||
function goBack() {
|
||||
if (typeof history !== 'undefined' && history.length > 1) history.back();
|
||||
else goto(resolve('/'));
|
||||
else goto(resolve(''));
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
@ -78,7 +78,7 @@
|
|||
|
||||
<!-- Primary actions -->
|
||||
<div class="flex flex-col items-center justify-center gap-3 sm:flex-row">
|
||||
<Button onclick={() => goto(resolve('/'))} class="gap-2">
|
||||
<Button onclick={() => goto(resolve(''))} class="gap-2">
|
||||
<Home class="h-4 w-4" />
|
||||
Go to dashboard
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script>
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import { refreshAll } from '$app/navigation';
|
||||
import { deserialize } from '$app/forms';
|
||||
import { Loader2, Upload, FileText, Download, CheckCircle2, AlertCircle } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||
import { downloadImportErrors } from '$lib/utils/csv.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import * as Dialog from '#lib/components/ui/dialog/index.js';
|
||||
import { downloadImportErrors } from '#lib/utils/csv.js';
|
||||
|
||||
/**
|
||||
* Two-step CSV import flow: upload → preview → confirm.
|
||||
|
|
@ -194,7 +194,7 @@
|
|||
if (result.type === 'success' && result.data?.importCommit) {
|
||||
commitResult = result.data.importCommit;
|
||||
step = 'done';
|
||||
await invalidateAll();
|
||||
await refreshAll();
|
||||
}
|
||||
} catch (err) {
|
||||
formError = err instanceof Error ? err.message : 'Import failed';
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<script>
|
||||
import { resolve } from '$app/paths';
|
||||
import { enhance } from '$app/forms';
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import { refreshAll } from '$app/navigation';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { Sliders, Loader2, Check } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { Input } from '$lib/components/ui/input/index.js';
|
||||
import { Label } from '$lib/components/ui/label/index.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import { Input } from '#lib/components/ui/input/index.js';
|
||||
import { Label } from '#lib/components/ui/label/index.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} CustomFieldDef
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
{#if isEmpty}
|
||||
<p class="text-sm text-[var(--text-secondary)]">
|
||||
No custom fields configured for {target.toLowerCase()}.
|
||||
<a class="underline" href={resolve(`/settings/custom-fields?target=${target}`)}>Manage</a>
|
||||
<a class="underline" href={resolve(`settings/custom-fields?target=${target}`)}>Manage</a>
|
||||
</p>
|
||||
{:else}
|
||||
<form
|
||||
|
|
@ -93,7 +93,7 @@
|
|||
saving = false;
|
||||
if (result.type === 'success') {
|
||||
toast.success(`${title} saved`);
|
||||
await invalidateAll();
|
||||
await refreshAll();
|
||||
} else if (result.type === 'failure') {
|
||||
toast.error(String(result.data?.error || 'Failed to save'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { SvelteDate } from 'svelte/reactivity';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import {
|
||||
Activity,
|
||||
Plus,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { resolve } from '$app/paths';
|
||||
import { asInternalPath } from '$lib/utils/paths.js';
|
||||
import { asInternalPath } from '#lib/utils/paths.js';
|
||||
import { AlertCircle, Calendar, Phone, Flame, Zap } from '@lucide/svelte';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script>
|
||||
import { SvelteDate } from 'svelte/reactivity';
|
||||
import { resolve } from '$app/paths';
|
||||
import { Badge } from '$lib/components/ui/badge/index.js';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { Badge } from '#lib/components/ui/badge/index.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import { Flame, Phone, Mail, ChevronRight, Calendar, Sparkles } from '@lucide/svelte';
|
||||
|
||||
/**
|
||||
|
|
@ -131,7 +131,7 @@
|
|||
{#each leads as lead (lead.id)}
|
||||
{@const config = ratingConfig[lead.rating || 'HOT'] || ratingConfig.HOT}
|
||||
<a
|
||||
href={resolve(`/leads?view=${lead.id}`)}
|
||||
href={resolve(`leads?view=${lead.id}`)}
|
||||
class="group flex items-center gap-3 px-5 py-3 transition-all duration-200 hover:bg-[var(--color-primary-light)] dark:hover:bg-[var(--color-primary-default)]/5"
|
||||
>
|
||||
<!-- Lead info -->
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { TrendingUp, TrendingDown } from '@lucide/svelte';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script>
|
||||
import { SvelteDate } from 'svelte/reactivity';
|
||||
import { resolve } from '$app/paths';
|
||||
import { Badge } from '$lib/components/ui/badge/index.js';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { Badge } from '#lib/components/ui/badge/index.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import { Circle, CheckCircle2, ChevronRight, ListTodo } from '@lucide/svelte';
|
||||
|
||||
/**
|
||||
|
|
@ -199,7 +199,7 @@
|
|||
{#each filteredTasks() as task (task.id)}
|
||||
{@const config = getPriorityConfig(task.priority)}
|
||||
<a
|
||||
href={resolve(`/tasks?view=${task.id}`)}
|
||||
href={resolve(`tasks?view=${task.id}`)}
|
||||
class="group flex items-center gap-3 px-5 py-3 transition-all duration-200 hover:bg-[var(--color-primary-light)] dark:hover:bg-[var(--color-primary-default)]/5"
|
||||
>
|
||||
<!-- Status icon -->
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||
import * as Sidebar from '#lib/components/ui/sidebar/index.js';
|
||||
import AppSidebar from './AppSidebar.svelte';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
<script>
|
||||
import { resolve } from '$app/paths';
|
||||
import { asInternalPath } from '$lib/utils/paths.js';
|
||||
import { asInternalPath } from '#lib/utils/paths.js';
|
||||
import { untrack } from 'svelte';
|
||||
import { page } from '$app/stores';
|
||||
import { page } from '$app/state';
|
||||
import { afterNavigate, goto } from '$app/navigation';
|
||||
|
||||
import * as Sidebar from '$lib/components/ui/sidebar/index.js';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
||||
import * as Collapsible from '$lib/components/ui/collapsible/index.js';
|
||||
import { Avatar, AvatarFallback } from '$lib/components/ui/avatar/index.js';
|
||||
import * as Sidebar from '#lib/components/ui/sidebar/index.js';
|
||||
import * as DropdownMenu from '#lib/components/ui/dropdown-menu/index.js';
|
||||
import * as Collapsible from '#lib/components/ui/collapsible/index.js';
|
||||
import { Avatar, AvatarFallback } from '#lib/components/ui/avatar/index.js';
|
||||
|
||||
import {
|
||||
Target,
|
||||
|
|
@ -50,7 +50,7 @@
|
|||
Smartphone,
|
||||
KeyRound
|
||||
} from '@lucide/svelte';
|
||||
import { Bell } from '$lib/components/notifications/index.js';
|
||||
import { Bell } from '#lib/components/notifications/index.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
|
|
@ -125,7 +125,7 @@
|
|||
localStorage.setItem('theme', newTheme);
|
||||
};
|
||||
|
||||
let currentPath = $derived($page.url.pathname);
|
||||
let currentPath = $derived(page.url.pathname);
|
||||
|
||||
/** @type {{ [key: string]: boolean }} */
|
||||
let openDropdowns = $state({});
|
||||
|
|
@ -380,7 +380,7 @@
|
|||
>
|
||||
<div class="flex items-center gap-2">
|
||||
<a
|
||||
href={resolve('/org')}
|
||||
href={resolve('org')}
|
||||
data-sveltekit-preload-data="off"
|
||||
class="flex min-w-0 flex-1 items-center gap-2.5 rounded-md px-1 py-1 transition-colors hover:bg-[color:var(--sidebar-accent)] focus-visible:ring-2 focus-visible:ring-[color:var(--sidebar-ring)] focus-visible:outline-none"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script>
|
||||
import { Filter } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { Badge } from '$lib/components/ui/badge/index.js';
|
||||
import * as Popover from '$lib/components/ui/popover/index.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import { Badge } from '#lib/components/ui/badge/index.js';
|
||||
import * as Popover from '#lib/components/ui/popover/index.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { resolve } from '$app/paths';
|
||||
import { asInternalPath } from '$lib/utils/paths.js';
|
||||
import { asInternalPath } from '#lib/utils/paths.js';
|
||||
import { ChevronRight } from '@lucide/svelte';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script>
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import { refreshAll } from '$app/navigation';
|
||||
import { deserialize } from '$app/forms';
|
||||
import { Loader2, Upload, FileText, Download, CheckCircle2, AlertCircle } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||
import { downloadImportErrors } from '$lib/utils/csv.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import * as Dialog from '#lib/components/ui/dialog/index.js';
|
||||
import { downloadImportErrors } from '#lib/utils/csv.js';
|
||||
|
||||
/**
|
||||
* Two-step CSV import flow: upload → preview → confirm.
|
||||
|
|
@ -190,7 +190,7 @@
|
|||
if (result.type === 'success' && result.data?.importCommit) {
|
||||
commitResult = result.data.importCommit;
|
||||
step = 'done';
|
||||
await invalidateAll();
|
||||
await refreshAll();
|
||||
}
|
||||
} catch (err) {
|
||||
formError = err instanceof Error ? err.message : 'Import failed';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script>
|
||||
import { Bell as BellIcon } from '@lucide/svelte';
|
||||
import * as Popover from '$lib/components/ui/popover/index.js';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { notifications } from '$lib/stores/notifications.svelte.js';
|
||||
import * as Popover from '#lib/components/ui/popover/index.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import { notifications } from '#lib/stores/notifications.svelte.js';
|
||||
import NotificationPanel from './NotificationPanel.svelte';
|
||||
|
||||
let open = $state(false);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@
|
|||
Trash2,
|
||||
UserPlus
|
||||
} from '@lucide/svelte';
|
||||
import { formatRelativeDate } from '$lib/utils/formatting.js';
|
||||
import { notifications } from '$lib/stores/notifications.svelte.js';
|
||||
import { formatRelativeDate } from '#lib/utils/formatting.js';
|
||||
import { notifications } from '#lib/stores/notifications.svelte.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script>
|
||||
import { invalidateAll } from '$app/navigation';
|
||||
import { refreshAll } from '$app/navigation';
|
||||
import { deserialize } from '$app/forms';
|
||||
import { Loader2, Upload, FileText, Download, CheckCircle2, AlertCircle } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||
import { downloadImportErrors } from '$lib/utils/csv.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import * as Dialog from '#lib/components/ui/dialog/index.js';
|
||||
import { downloadImportErrors } from '#lib/utils/csv.js';
|
||||
|
||||
/**
|
||||
* Two-step CSV import flow: upload → preview → confirm.
|
||||
|
|
@ -174,7 +174,7 @@
|
|||
if (result.type === 'success' && result.data?.importCommit) {
|
||||
commitResult = result.data.importCommit;
|
||||
step = 'done';
|
||||
await invalidateAll();
|
||||
await refreshAll();
|
||||
}
|
||||
} catch (err) {
|
||||
formError = err instanceof Error ? err.message : 'Import failed';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { buttonVariants } from '#lib/components/ui/button/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { buttonVariants } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { buttonVariants } from '#lib/components/ui/button/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import AlertDialogOverlay from './alert-dialog-overlay.svelte';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { AlertDialog as AlertDialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { gradientFor, WORKSPACE_GRADIENT } from './avatar-gradients.js';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Avatar as AvatarPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @typedef {'loading' | 'loaded' | 'error'} LoadingStatus
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
</script>
|
||||
|
||||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { X } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
|
||||
/** @typedef {{ label: string, onClick: () => void, variant?: 'default'|'outline'|'destructive' }} BulkAction */
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script module>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { tv } from 'tailwind-variants';
|
||||
|
||||
export const buttonVariants = tv({
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
<script>
|
||||
import { resolve } from '$app/paths';
|
||||
import { asInternalPath } from '$lib/utils/paths.js';
|
||||
import { asInternalPath } from '#lib/utils/paths.js';
|
||||
/**
|
||||
* @type {{
|
||||
* class?: string,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { Calendar } from 'bits-ui';
|
||||
import { ChevronLeft, ChevronRight } from '@lucide/svelte';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { today, getLocalTimeZone } from '@internationalized/date';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
/** @type {{ ref?: HTMLElement | null, class?: string, children?: import('svelte').Snippet, [key: string]: any }} */
|
||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className = undefined, children, ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
let { ref = $bindable(null), class: className = undefined, children, ...restProps } = $props();
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
let { ref = $bindable(null), class: className = undefined, children, ...restProps } = $props();
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<script>
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { MessageSquare, Send, Trash2, Loader2, ChevronDown, ChevronUp } from '@lucide/svelte';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { Textarea } from '$lib/components/ui/textarea/index.js';
|
||||
import * as AlertDialog from '$lib/components/ui/alert-dialog/index.js';
|
||||
import { formatRelativeDate, getInitials } from '$lib/utils/formatting.js';
|
||||
import * as api from '$lib/api.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import { Textarea } from '#lib/components/ui/textarea/index.js';
|
||||
import * as AlertDialog from '#lib/components/ui/alert-dialog/index.js';
|
||||
import { formatRelativeDate, getInitials } from '#lib/utils/formatting.js';
|
||||
import * as api from '#lib/api.js';
|
||||
import MentionBody from './MentionBody.svelte';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { SvelteMap } from 'svelte/reactivity';
|
||||
import { resolve } from '$app/paths';
|
||||
import { asInternalPath } from '$lib/utils/paths.js';
|
||||
import { asInternalPath } from '#lib/utils/paths.js';
|
||||
/**
|
||||
* Render a comment body with `@username` tokens upgraded to styled chips.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { Textarea } from '$lib/components/ui/textarea/index.js';
|
||||
import { Textarea } from '#lib/components/ui/textarea/index.js';
|
||||
|
||||
/**
|
||||
* Textarea with `@username` typeahead. Accepts the same props as the
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script>
|
||||
import { resolve } from '$app/paths';
|
||||
import { asInternalPath } from '$lib/utils/paths.js';
|
||||
import { asInternalPath } from '#lib/utils/paths.js';
|
||||
import { X, Trash2, Maximize2 } from '@lucide/svelte';
|
||||
import * as Sheet from '$lib/components/ui/sheet/index.js';
|
||||
import { Skeleton } from '$lib/components/ui/skeleton/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import * as Sheet from '#lib/components/ui/sheet/index.js';
|
||||
import { Skeleton } from '#lib/components/ui/skeleton/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import CrmPropertyRow from './CrmPropertyRow.svelte';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<script>
|
||||
import { Check, Calendar as CalendarIcon } from '@lucide/svelte';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
||||
import * as Popover from '$lib/components/ui/popover/index.js';
|
||||
import { Calendar } from '$lib/components/ui/calendar/index.js';
|
||||
import { EditableMultiSelect } from '$lib/components/ui/editable-field/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { formatCurrency } from '$lib/utils/formatting.js';
|
||||
import * as DropdownMenu from '#lib/components/ui/dropdown-menu/index.js';
|
||||
import * as Popover from '#lib/components/ui/popover/index.js';
|
||||
import { Calendar } from '#lib/components/ui/calendar/index.js';
|
||||
import { EditableMultiSelect } from '#lib/components/ui/editable-field/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { formatCurrency } from '#lib/utils/formatting.js';
|
||||
import { parseDate } from '@internationalized/date';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<script>
|
||||
import { tick } from 'svelte';
|
||||
import { Check, ChevronDown, Building2, User } from '@lucide/svelte';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
||||
import { getOptionStyle, getOptionLabel, getOptionBgColor } from '$lib/utils/table-helpers.js';
|
||||
import { formatCurrency } from '$lib/utils/formatting.js';
|
||||
import * as DropdownMenu from '#lib/components/ui/dropdown-menu/index.js';
|
||||
import { getOptionStyle, getOptionLabel, getOptionBgColor } from '#lib/utils/table-helpers.js';
|
||||
import { formatCurrency } from '#lib/utils/formatting.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} ColumnDef
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import DialogPortal from './dialog-portal.svelte';
|
||||
import XIcon from '@lucide/svelte/icons/x';
|
||||
import * as Dialog from './index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className = undefined,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className = undefined, ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
let { ref = $bindable(null), class: className = undefined, children, ...restProps } = $props();
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className = undefined, children, ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Dialog as DialogPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className = undefined, ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import CheckIcon from '@lucide/svelte/icons/check';
|
||||
import MinusIcon from '@lucide/svelte/icons/minus';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
checked = $bindable(false),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
/**
|
||||
* @type {{
|
||||
* ref?: HTMLElement | null,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<script>
|
||||
import { ChevronDown, X, Search, AlertCircle, RotateCw } from '@lucide/svelte';
|
||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
||||
import { Badge } from '$lib/components/ui/badge/index.js';
|
||||
import { Skeleton } from '$lib/components/ui/skeleton/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import * as DropdownMenu from '#lib/components/ui/dropdown-menu/index.js';
|
||||
import { Badge } from '#lib/components/ui/badge/index.js';
|
||||
import { Skeleton } from '#lib/components/ui/skeleton/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} SelectOption
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<script>
|
||||
import { CalendarDays, ChevronDown, X, Clock, ArrowRight } from '@lucide/svelte';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import * as Popover from '$lib/components/ui/popover/index.js';
|
||||
import Calendar from '$lib/components/ui/calendar/Calendar.svelte';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import * as Popover from '#lib/components/ui/popover/index.js';
|
||||
import Calendar from '#lib/components/ui/calendar/Calendar.svelte';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
import { today, getLocalTimeZone, parseDate } from '@internationalized/date';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { Filter, X, ChevronDown, Sparkles } from '@lucide/svelte';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { Button } from '#lib/components/ui/button/index.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { ChevronDown, Check, X } from '@lucide/svelte';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import * as Popover from '$lib/components/ui/popover/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import * as Popover from '#lib/components/ui/popover/index.js';
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { ChevronDown, Check, X, Tag, Hash } from '@lucide/svelte';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import * as Popover from '$lib/components/ui/popover/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import * as Popover from '#lib/components/ui/popover/index.js';
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { enhance } from '$app/forms';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @typedef {Object} Props
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
* @property {string} [action]
|
||||
* @property {'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain'} [enctype]
|
||||
* @property {(this: HTMLFormElement, event: SubmitEvent) => void} [onsubmit]
|
||||
* @property {import('@sveltejs/kit').SubmitFunction} [useEnhance]
|
||||
* @property {import('$app/forms').SubmitFunction} [useEnhance]
|
||||
* @property {string} [class]
|
||||
* @property {string} [errorMessage]
|
||||
* @property {import('svelte').Snippet} children
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Label as LabelPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className = undefined, ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { ChevronLeft, ChevronRight, ChevronsUpDown } from '@lucide/svelte';
|
||||
import * as Popover from '$lib/components/ui/popover/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import * as Popover from '#lib/components/ui/popover/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { Popover as PopoverPrimitive } from 'bits-ui';
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { Popover as PopoverPrimitive } from 'bits-ui';
|
||||
|
||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Progress as ProgressPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className, max = 100, value, ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import SelectPortal from './select-portal.svelte';
|
||||
import SelectScrollUpButton from './select-scroll-up-button.svelte';
|
||||
import SelectScrollDownButton from './select-scroll-down-button.svelte';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className = '',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Select as SelectPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import CheckIcon from '@lucide/svelte/icons/check';
|
||||
import { Select as SelectPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
let { ref = $bindable(null), class: className, children, ...restProps } = $props();
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
|
||||
import { Select as SelectPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className = '', ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import ChevronUpIcon from '@lucide/svelte/icons/chevron-up';
|
||||
import { Select as SelectPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className = '', ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Separator } from '$lib/components/ui/separator/index.js';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { Separator } from '#lib/components/ui/separator/index.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let { ref = $bindable(null), class: className, ...restProps } = $props();
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { Select as SelectPrimitive } from 'bits-ui';
|
||||
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Separator as SeparatorPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<script>
|
||||
import { Dialog as SheetPrimitive } from 'bits-ui';
|
||||
import SheetOverlay from './sheet-overlay.svelte';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Dialog as SheetPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Dialog as SheetPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { Dialog as SheetPrimitive } from 'bits-ui';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
|
||||
/**
|
||||
* @type {{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { X, ChevronLeft } from '@lucide/svelte';
|
||||
import { cn } from '$lib/utils.js';
|
||||
import { cn } from '#lib/utils.js';
|
||||
import { sidePanelHidden } from './index.js';
|
||||
|
||||
/**
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue