Compare commits

..

1 Commits

Author SHA1 Message Date
lebaudantoine ed84ffa410 💬(frontend) rename Agent Connect mentions
Feedback from C Chausse. Outdated copy writting.
2024-11-19 14:18:56 +01:00
124 changed files with 2158 additions and 10472 deletions
-47
View File
@@ -117,57 +117,10 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-and-push-summary:
runs-on: ubuntu-latest
steps:
-
uses: actions/create-github-app-token@v1
id: app-token
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: "meet,secrets"
-
name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
token: ${{ steps.app-token.outputs.token }}
-
name: Load sops secrets
uses: rouja/actions-sops@main
with:
secret-file: secrets/numerique-gouv/meet/secrets.enc.env
age-key: ${{ secrets.SOPS_PRIVATE }}
-
name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: lasuite/meet-summary
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
run: echo "$DOCKER_HUB_PASSWORD" | docker login -u "$DOCKER_HUB_USER" --password-stdin
-
name: Build and push
uses: docker/build-push-action@v6
with:
context: ./src/summary
file: ./src/summary/Dockerfile
target: production
build-args: DOCKER_USER=${{ env.DOCKER_USER }}:-1000
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
notify-argocd:
needs:
- build-and-push-frontend
- build-and-push-backend
- build-and-push-summary
runs-on: ubuntu-latest
if: |
github.event_name != 'pull_request'
+1
View File
@@ -302,6 +302,7 @@ build-k8s-cluster: ## build the kubernetes cluster using kind
.PHONY: build-k8s-cluster
start-tilt: ## start the kubernetes cluster using kind
kubectl config set-context --current --namespace=meet
tilt up -f ./bin/Tiltfile
.PHONY: build-k8s-cluster
+1 -11
View File
@@ -1,6 +1,7 @@
load('ext://uibutton', 'cmd_button', 'bool_input', 'location')
load('ext://namespace', 'namespace_create', 'namespace_inject')
namespace_create('meet')
docker_build(
'localhost:5001/meet-backend:latest',
context='..',
@@ -27,17 +28,6 @@ docker_build(
]
)
docker_build(
'localhost:5001/meet-summary:latest',
context='../src/summary',
dockerfile='../src/summary/Dockerfile',
only=['.', '../../docker', '../../.dockerignore'],
target = 'production',
live_update=[
sync('../src/summary', '/home/summary'),
]
)
k8s_yaml(local('cd ../src/helm && helmfile -n meet -e dev template .'))
migration = '''
+6 -7
View File
@@ -29,7 +29,7 @@ echo "2. Create kind cluster with containerd registry config dir enabled"
# https://github.com/kubernetes-sigs/kind/issues/2875
# https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration
# See: https://github.com/containerd/containerd/blob/main/docs/hosts.md
cat <<EOF | kind create cluster --name visio --config=-
cat <<EOF | kind create cluster --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
@@ -52,6 +52,10 @@ nodes:
- containerPort: 443
hostPort: 443
protocol: TCP
- role: worker
image: kindest/node:v1.27.3
- role: worker
image: kindest/node:v1.27.3
EOF
echo "3. Add the registry config to the nodes"
@@ -64,7 +68,7 @@ echo "3. Add the registry config to the nodes"
# We want a consistent name that works from both ends, so we tell containerd to
# alias localhost:${reg_port} to the registry container when pulling images
REGISTRY_DIR="/etc/containerd/certs.d/localhost:${reg_port}"
for node in $(kind get nodes --name visio); do
for node in $(kind get nodes); do
docker exec "${node}" mkdir -p "${REGISTRY_DIR}"
cat <<EOF | docker exec -i "${node}" cp /dev/stdin "${REGISTRY_DIR}/hosts.toml"
[host."http://${reg_name}:5000"]
@@ -132,8 +136,3 @@ echo "6. Install ingress-nginx"
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
kubectl -n ingress-nginx create secret tls mkcert --key /tmp/127.0.0.1.nip.io+1-key.pem --cert /tmp/127.0.0.1.nip.io+1.pem
kubectl -n ingress-nginx patch deployments.apps ingress-nginx-controller --type 'json' -p '[{"op": "add", "path": "/spec/template/spec/containers/0/args/-", "value":"--default-ssl-certificate=ingress-nginx/mkcert"}]'
echo "7. Setup namespace"
kubectl create ns meet
kubectl config set-context --current --namespace=meet
kubectl -n meet create secret generic mkcert --from-file=rootCA.pem="$(mkcert -CAROOT)/rootCA.pem"
+1 -1
Submodule secrets updated: 142e7a70b1...8ef9f4513a
+1 -12
View File
@@ -32,7 +32,6 @@ from core.recording.event.exceptions import (
InvalidFileTypeError,
ParsingEventDataError,
)
from core.recording.event.notification import notification_service
from core.recording.event.parsers import get_parser
from core.recording.worker.exceptions import (
RecordingStartError,
@@ -449,17 +448,7 @@ class RecordingViewSet(
" in an error state or has already been saved."
)
# Attempt to notify external services about the recording
# This is a non-blocking operation - failures are logged but don't interrupt the flow
notification_succeeded = notification_service.notify_external_services(
recording
)
recording.status = (
models.RecordingStatusChoices.NOTIFICATION_SUCCEEDED
if notification_succeeded
else models.RecordingStatusChoices.SAVED
)
recording.status = models.RecordingStatusChoices.SAVED
recording.save()
return drf_response.Response(
@@ -1,18 +0,0 @@
# Generated by Django 5.1.3 on 2024-12-02 13:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0008_user_full_name_user_short_name'),
]
operations = [
migrations.AlterField(
model_name='recording',
name='status',
field=models.CharField(choices=[('initiated', 'Initiated'), ('active', 'Active'), ('stopped', 'Stopped'), ('saved', 'Saved'), ('aborted', 'Aborted'), ('failed_to_start', 'Failed to Start'), ('failed_to_stop', 'Failed to Stop'), ('notification_succeeded', 'Notification succeeded')], default='initiated', max_length=50),
),
]
+18 -20
View File
@@ -49,7 +49,6 @@ class RecordingStatusChoices(models.TextChoices):
ABORTED = "aborted", _("Aborted")
FAILED_TO_START = "failed_to_start", _("Failed to Start")
FAILED_TO_STOP = "failed_to_stop", _("Failed to Stop")
NOTIFICATION_SUCCEEDED = "notification_succeeded", _("Notification succeeded")
@classmethod
def is_final(cls, status):
@@ -463,23 +462,7 @@ class BaseAccess(BaseModel):
class Recording(BaseModel):
"""Model for recordings that take place in a room.
Recording Status Flow:
1. INITIATED: Initial state when recording is requested
2. ACTIVE: Recording is currently in progress
3. STOPPED: Recording has been stopped by user/system
4. SAVED: Recording has been successfully processed and stored
4. NOTIFICATION_SUCCEEDED: External service has been notified of this recording
Error States:
- FAILED_TO_START: Worker failed to initialize recording
- FAILED_TO_STOP: Worker failed during stop operation
- ABORTED: Recording was terminated before completion
Warning: Worker failures may lead to database inconsistency between the actual
recording state and its status in the database.
"""
"""Model for recordings that take place in a room"""
room = models.ForeignKey(
Room,
@@ -488,7 +471,7 @@ class Recording(BaseModel):
verbose_name=_("Room"),
)
status = models.CharField(
max_length=50,
max_length=20,
choices=RecordingStatusChoices.choices,
default=RecordingStatusChoices.INITIATED,
)
@@ -560,7 +543,22 @@ class Recording(BaseModel):
class RecordingAccess(BaseAccess):
"""Relation model to give access to a recording for a user or a team with a role."""
"""Relation model to give access to a recording for a user or a team with a role.
Recording Status Flow:
1. INITIATED: Initial state when recording is requested
2. ACTIVE: Recording is currently in progress
3. STOPPED: Recording has been stopped by user/system
4. SAVED: Recording has been successfully processed and stored
Error States:
- FAILED_TO_START: Worker failed to initialize recording
- FAILED_TO_STOP: Worker failed during stop operation
- ABORTED: Recording was terminated before completion
Warning: Worker failures may lead to database inconsistency between the actual
recording state and its status in the database.
"""
recording = models.ForeignKey(
Recording,
@@ -47,6 +47,8 @@ class StorageEventAuthentication(BaseAuthentication):
def authenticate(self, request):
"""Validate the Bearer token from the Authorization header."""
if not settings.RECORDING_ENABLE_STORAGE_EVENT_AUTH:
return MachineUser(), None
if not settings.RECORDING_ENABLE_STORAGE_EVENT_AUTH:
return MachineUser(), None
@@ -1,93 +0,0 @@
"""Service to notify external services when a new recording is ready."""
import logging
from django.conf import settings
import requests
from core import models
logger = logging.getLogger(__name__)
class NotificationService:
"""Service for processing recordings and notifying external services."""
def notify_external_services(self, recording):
"""Process a recording based on its mode."""
if recording.mode == models.RecordingModeChoices.TRANSCRIPT:
return self._notify_summary_service(recording)
if recording.mode == models.RecordingModeChoices.SCREEN_RECORDING:
logger.warning(
"Screen recording mode not implemented for recording %s", recording.id
)
return False
logger.error(
"Unknown recording mode %s for recording %s",
recording.mode,
recording.id,
)
return False
@staticmethod
def _notify_summary_service(recording):
"""Notify summary service about a new recording."""
if (
not settings.SUMMARY_SERVICE_ENDPOINT
or not settings.SUMMARY_SERVICE_API_TOKEN
):
logger.error("Summary service not configured")
return False
owner_access = (
models.RecordingAccess.objects.select_related("user")
.filter(
role=models.RoleChoices.OWNER,
recording_id=recording.id,
)
.first()
)
if not owner_access:
logger.error("No owner found for recording %s", recording.id)
return False
key = f"{settings.RECORDING_OUTPUT_FOLDER}/{recording.id}.ogg"
payload = {
"filename": key,
"email": owner_access.user.email,
"sub": owner_access.user.sub,
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {settings.SUMMARY_SERVICE_API_TOKEN}",
}
try:
response = requests.post(
settings.SUMMARY_SERVICE_ENDPOINT,
json=payload,
headers=headers,
timeout=30,
)
response.raise_for_status()
except requests.HTTPError as exc:
logger.exception(
"Summary service HTTP error for recording %s. URL: %s. Exception: %s",
recording.id,
settings.SUMMARY_SERVICE_ENDPOINT,
exc,
)
return False
return True
notification_service = NotificationService()
Binary file not shown.
Binary file not shown.
+1 -7
View File
@@ -446,13 +446,7 @@ class Base(Configuration):
False, environ_name="RECORDING_STORAGE_EVENT_ENABLE", environ_prefix=None
)
RECORDING_STORAGE_EVENT_TOKEN = values.Value(
None, environ_name="RECORDING_STORAGE_EVENT_TOKEN", environ_prefix=None
)
SUMMARY_SERVICE_ENDPOINT = values.Value(
None, environ_name="SUMMARY_SERVICE_ENDPOINT", environ_prefix=None
)
SUMMARY_SERVICE_API_TOKEN = values.Value(
None, environ_name="SUMMARY_SERVICE_API_TOKEN", environ_prefix=None
None, environ_name="RECORDING_STORAGE_HOOK_TOKEN", environ_prefix=None
)
# pylint: disable=invalid-name
+19 -19
View File
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "meet"
version = "0.1.9"
version = "0.1.8"
authors = [{ "name" = "DINUM", "email" = "dev@mail.numerique.gouv.fr" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
@@ -25,18 +25,18 @@ license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"boto3==1.35.68",
"boto3==1.35.19",
"Brotli==1.1.0",
"celery[redis]==5.4.0",
"django-configurations==2.5.1",
"django-cors-headers==4.6.0",
"django-cors-headers==4.4.0",
"django-countries==7.6.1",
"django-parler==2.3",
"redis==5.2.0",
"redis==5.0.8",
"django-redis==5.4.0",
"django-storages[s3]==1.14.4",
"django-timezone-field>=5.1",
"django==5.1.3",
"django==5.1.1",
"djangorestframework==3.15.2",
"drf_spectacular==0.27.2",
"dockerflow==2024.4.2",
@@ -47,17 +47,17 @@ dependencies = [
"june-analytics-python==2.3.0",
"markdown==3.7",
"nested-multipart-parser==1.5.0",
"psycopg[binary]==3.2.3",
"PyJWT==2.10.0",
"psycopg[binary]==3.2.2",
"PyJWT==2.9.0",
"python-frontmatter==1.1.0",
"requests==2.32.3",
"sentry-sdk==2.19.0",
"sentry-sdk==2.14.0",
"url-normalize==1.4.3",
"WeasyPrint>=60.2",
"whitenoise==6.8.2",
"whitenoise==6.7.0",
"mozilla-django-oidc==4.0.1",
"livekit-api==0.8.0",
"aiohttp==3.11.7",
"livekit-api==0.7.0",
"aiohttp==3.10.10",
]
[project.urls]
@@ -69,21 +69,21 @@ dependencies = [
[project.optional-dependencies]
dev = [
"django-extensions==3.2.3",
"drf-spectacular-sidecar==2024.11.1",
"drf-spectacular-sidecar==2024.7.1",
"freezegun==1.5.1",
"ipdb==0.13.13",
"ipython==8.29.0",
"pyfakefs==5.7.1",
"pylint-django==2.6.1",
"pylint==3.3.1",
"pytest-cov==6.0.0",
"ipython==8.27.0",
"pyfakefs==5.6.0",
"pylint-django==2.5.5",
"pylint==3.2.7",
"pytest-cov==5.0.0",
"pytest-django==4.9.0",
"pytest==8.3.3",
"pytest-icdiff==0.9",
"pytest-xdist==3.6.1",
"responses==0.25.3",
"ruff==0.8.0",
"types-requests==2.32.0.20241016",
"ruff==0.6.5",
"types-requests==2.32.0.20240914",
]
[tool.setuptools]
+1 -1
View File
@@ -2,7 +2,7 @@
<html lang="en" data-lk-theme="visio-light">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/assets/icon.svg" />
<link rel="icon" type="image/svg+xml" href="/play-icon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Visio</title>
</head>
+1673 -1571
View File
File diff suppressed because it is too large Load Diff
+29 -29
View File
@@ -1,7 +1,7 @@
{
"name": "meet",
"private": true,
"version": "0.1.9",
"version": "0.1.8",
"type": "module",
"scripts": {
"dev": "panda codegen && vite",
@@ -13,48 +13,48 @@
"check": "prettier --check ./src"
},
"dependencies": {
"@livekit/components-react": "2.6.9",
"@livekit/components-styles": "1.1.4",
"@livekit/components-react": "2.6.5",
"@livekit/components-styles": "1.1.3",
"@livekit/track-processors": "0.3.2",
"@pandacss/preset-panda": "0.48.0",
"@react-aria/toast": "3.0.0-beta.18",
"@remixicon/react": "4.5.0",
"@tanstack/react-query": "5.61.3",
"@pandacss/preset-panda": "0.46.1",
"@react-aria/toast": "3.0.0-beta.16",
"@remixicon/react": "4.2.0",
"@tanstack/react-query": "5.59.4",
"crisp-sdk-web": "1.0.25",
"hoofd": "1.7.1",
"i18next": "24.0.2",
"i18next": "23.15.2",
"i18next-browser-languagedetector": "8.0.0",
"i18next-parser": "9.0.2",
"i18next-resources-to-backend": "1.2.1",
"livekit-client": "2.6.3",
"posthog-js": "1.188.0",
"livekit-client": "2.5.7",
"posthog-js": "1.186.1",
"react": "18.3.1",
"react-aria-components": "1.5.0",
"react-aria-components": "1.4.0",
"react-dom": "18.3.1",
"react-i18next": "15.1.1",
"react-i18next": "15.0.2",
"use-sound": "4.0.3",
"valtio": "2.1.2",
"valtio": "2.0.0",
"wouter": "3.3.5"
},
"devDependencies": {
"@pandacss/dev": "0.48.0",
"@tanstack/eslint-plugin-query": "5.61.3",
"@tanstack/react-query-devtools": "5.61.3",
"@types/node": "22.9.3",
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
"@typescript-eslint/eslint-plugin": "8.15.0",
"@typescript-eslint/parser": "8.15.0",
"@vitejs/plugin-react": "4.3.3",
"@pandacss/dev": "0.46.1",
"@tanstack/eslint-plugin-query": "5.59.2",
"@tanstack/react-query-devtools": "5.59.4",
"@types/node": "20.16.11",
"@types/react": "18.3.11",
"@types/react-dom": "18.3.0",
"@typescript-eslint/eslint-plugin": "8.8.1",
"@typescript-eslint/parser": "8.8.1",
"@vitejs/plugin-react": "4.3.2",
"eslint": "8.57.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-react-hooks": "5.0.0",
"eslint-plugin-react-refresh": "0.4.14",
"postcss": "8.4.49",
"eslint-plugin-jsx-a11y": "6.10.0",
"eslint-plugin-react-hooks": "4.6.2",
"eslint-plugin-react-refresh": "0.4.12",
"postcss": "8.4.47",
"prettier": "3.3.3",
"typescript": "5.7.2",
"vite": "5.4.11",
"vite-tsconfig-paths": "5.1.3"
"typescript": "5.6.3",
"vite": "5.4.8",
"vite-tsconfig-paths": "5.0.1"
}
}
+1 -95
View File
@@ -35,40 +35,6 @@ const config: Config = {
exclude: [],
jsxFramework: 'react',
outdir: 'src/styled-system',
globalFontface: {
Marianne: [
{
src: 'url(/fonts/Marianne-Regular-subset.woff2) format("woff2")',
fontWeight: 400,
fontStyle: 'normal',
fontDisplay: 'swap',
},
{
path: 'url(/fonts/Marianne-Regular_Italic-subset.woff2) format("woff2")',
fontWeight: 400,
fontStyle: 'italic',
fontDisplay: 'swap',
},
{
path: 'url(/fonts/Marianne-Medium-subset.woff2) format("woff2")',
fontWeight: 500,
fontStyle: 'normal',
fontDisplay: 'swap',
},
{
path: 'url(/fonts/Marianne-Bold-subset.woff2) format("woff2")',
fontWeight: 700,
fontStyle: 'normal',
fontDisplay: 'swap',
},
{
path: 'url(/fonts/Marianne-ExtraBold-subset.woff2) format("woff2")',
fontWeight: 800,
fontStyle: 'normal',
fontDisplay: 'swap',
},
],
},
theme: {
...pandaPreset.theme,
// media queries are defined in em so that zooming with text-only mode triggers breakpoints
@@ -127,66 +93,6 @@ const config: Config = {
* This way we'll only add the things we need step by step and prevent using lots of differents things.
*/
...pandaPreset.theme.tokens,
colors: defineTokens.colors({
...pandaPreset.theme.tokens.colors,
primaryDark: {
50: { value: '#161622' },
100: { value: '#2D2D46' },
200: { value: '#43436A' },
300: { value: '#5A5A8F' },
400: { value: '#7070B3' },
500: { value: '#8787D7' },
600: { value: '#9D9DDF' },
700: { value: '#B3B3E7' },
800: { value: '#C9C9EE' },
900: { value: '#DFDFF6' },
950: { value: '#F5F5FE' },
action: { value: '#C1C1FB' },
},
primary: {
50: { value: '#F5F5FE' },
100: { value: '#ECECFE' },
200: { value: '#E3E3FB' },
300: { value: '#CACAFB' },
400: { value: '#8585F6' },
500: { value: '#6A6AF4' },
600: { value: '#313178' },
700: { value: '#272747' },
800: { value: '#000091' },
900: { value: '#21213F' },
950: { value: '#1B1B35' },
action: { value: '#1212FF' },
},
greyscale: {
'000': { value: '#FFFFFF' },
50: { value: '#F6F6F6' },
100: { value: '#EEEEEE' },
200: { value: '#E5E5E5' },
250: { value: '#DDDDDD' },
300: { value: '#CECECE' },
400: { value: '#929292' },
500: { value: '#7C7C7C' },
600: { value: '#666666' },
700: { value: '#3A3A3A' },
750: { value: '#353535' },
800: { value: '#2A2A2A' },
900: { value: '#242424' },
950: { value: '#1E1E1E' },
1000: { value: '#161616' },
},
error: {
100: { value: '#261212' },
200: { value: '#6C302E' },
300: { value: '#983533' },
400: { value: '#CA3632' },
500: { value: '#EF413D' },
600: { value: '#EE6A66' },
700: { value: '#F28D8A' },
800: { value: '#F6AFAD' },
900: { value: '#FAD2D1' },
950: { value: '#FFF4F4' },
},
}),
animations: {},
blurs: {},
/* just directly use values as tokens. This allows us to follow a specific design scale,
@@ -288,7 +194,7 @@ const config: Config = {
semanticTokens: defineSemanticTokens({
colors: {
default: {
text: { value: '{colors.greyscale.1000}' },
text: { value: '{colors.gray.900}' },
bg: { value: 'white' },
subtle: { value: '{colors.gray.100}' },
'subtle-text': { value: '{colors.gray.600}' },
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

-5
View File
@@ -1,5 +0,0 @@
<svg width="160" height="160" viewBox="0 0 160 160" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.61135 59.6951C5 62.832 5 66.6949 5 73.1656V88.7307C5 96.7588 5 100.773 6.16756 104.366C7.20062 107.546 8.89041 110.472 11.1274 112.957C13.6555 115.765 17.1318 117.772 24.0842 121.786L37.564 129.568C43.7169 133.121 47.1472 135.101 50.4165 136.054V88.0288C50.4165 85.6281 49.1052 83.4192 46.9976 82.2696L5.61135 59.6951Z" fill="#C9191E"/>
<path d="M118.313 66.7489V91.7898C118.313 95.2521 119.818 98.5435 122.436 100.809L140.459 116.406C141.513 117.298 142.608 118.028 143.744 118.596C144.92 119.123 146.056 119.387 147.151 119.387C149.503 119.387 151.389 118.616 152.809 117.075C154.269 115.493 154.999 113.445 154.999 110.93V47.6577C154.999 45.1431 154.269 43.1151 152.809 41.5738C151.389 39.992 149.503 39.2011 147.151 39.2011C146.056 39.2011 144.92 39.4647 143.744 39.992C142.608 40.5193 141.513 41.2494 140.459 42.1822L122.45 57.7172C119.823 59.983 118.313 63.28 118.313 66.7489Z" fill="#000091"/>
<path d="M11.6345 50.7522C11.1333 50.4788 10.6078 50.2937 10.0757 50.1915C10.4114 49.7628 10.7622 49.3452 11.1276 48.9394C13.6558 46.1315 17.132 44.1245 24.0845 40.1105L37.5643 32.3279C44.5167 28.3139 47.993 26.3069 51.6887 25.5213C54.9587 24.8262 58.3383 24.8262 61.6083 25.5213C65.304 26.3069 68.7803 28.3139 75.7327 32.3279L89.0535 40.0187C89.1066 40.0492 89.1597 40.0798 89.2129 40.1105C96.1653 44.1245 99.6416 46.1316 102.17 48.9394C104.407 51.4238 106.096 54.3506 107.13 57.5301C108.297 61.1235 108.297 65.1375 108.297 73.1656V88.7307C108.297 96.7588 108.297 100.773 107.13 104.366C106.096 107.546 104.407 110.473 102.17 112.957C99.6416 115.765 96.1655 117.772 89.2133 121.785C89.1537 121.82 89.0936 121.855 89.0341 121.889L75.7327 129.568C68.7803 133.582 65.304 135.589 61.6083 136.375C61.4564 136.407 61.3043 136.438 61.1519 136.467L61.1519 88.0288C61.1519 81.6997 57.6948 75.8761 52.1386 72.8455L11.6345 50.7522Z" fill="#000091"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

-3
View File
@@ -1,3 +0,0 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.66667 4.00098V5.33431H3.33333V12.6676H10.6667V9.33431H12V13.3343C12 13.7025 11.7015 14.001 11.3333 14.001H2.66667C2.29848 14.001 2 13.7025 2 13.3343V4.66764C2 4.29945 2.29848 4.00098 2.66667 4.00098H6.66667ZM14 2.00098V7.33431H12.6667V4.27631L7.47133 9.47231L6.52867 8.52964L11.7233 3.33431H8.66667V2.00098H14Z" fill="#666666"/>
</svg>

Before

Width:  |  Height:  |  Size: 484 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -30,7 +30,7 @@ function App() {
<Route component={NotFoundScreen} />
</Switch>
</Layout>
<ReactQueryDevtools initialIsOpen={false} buttonPosition="top-left" />
<ReactQueryDevtools initialIsOpen={false} />
</I18nProvider>
</Suspense>
</QueryClientProvider>
+6 -1
View File
@@ -1,7 +1,12 @@
import { fetchApi } from './fetchApi'
import { keys } from './queryKeys'
import { useQuery } from '@tanstack/react-query'
import { RecordingMode } from '@/features/rooms/api/startRecording'
// todo - refactor it in a proper place
export enum RecordingMode {
Transcript = 'transcript',
ScreenRecording = 'screen_recording',
}
export interface ApiConfig {
analytics?: {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 651 KiB

After

Width:  |  Height:  |  Size: 635 KiB

-6
View File
@@ -1,6 +0,0 @@
<svg width="448" height="172" viewBox="0 0 448 172" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.61135 65.6951C5 68.832 5 72.6949 5 79.1656V94.7307C5 102.759 5 106.773 6.16756 110.366C7.20062 113.546 8.89041 116.472 11.1274 118.957C13.6555 121.765 17.1318 123.772 24.0842 127.786L37.564 135.568C43.7169 139.121 47.1472 141.101 50.4165 142.054V94.0288C50.4165 91.6281 49.1052 89.4192 46.9976 88.2696L5.61135 65.6951Z" fill="#C9191E"/>
<path d="M118.313 72.7489V97.7898C118.313 101.252 119.818 104.544 122.436 106.809L140.459 122.406C141.513 123.298 142.608 124.028 143.744 124.596C144.92 125.123 146.056 125.387 147.151 125.387C149.503 125.387 151.389 124.616 152.809 123.075C154.269 121.493 154.999 119.445 154.999 116.93V53.6577C154.999 51.1431 154.269 49.1151 152.809 47.5738C151.389 45.992 149.503 45.2011 147.151 45.2011C146.056 45.2011 144.92 45.4647 143.744 45.992C142.608 46.5193 141.513 47.2494 140.459 48.1822L122.45 63.7172C119.823 65.983 118.313 69.28 118.313 72.7489Z" fill="#000091"/>
<path d="M11.6345 56.7522C11.1333 56.4788 10.6078 56.2937 10.0757 56.1915C10.4114 55.7628 10.7622 55.3452 11.1276 54.9394C13.6558 52.1315 17.132 50.1245 24.0845 46.1105L37.5643 38.3279C44.5167 34.3139 47.993 32.3069 51.6887 31.5213C54.9587 30.8262 58.3383 30.8262 61.6083 31.5213C65.304 32.3069 68.7803 34.3139 75.7327 38.3279L89.0535 46.0187C89.1066 46.0492 89.1597 46.0798 89.2129 46.1105C96.1653 50.1245 99.6416 52.1316 102.17 54.9394C104.407 57.4238 106.096 60.3506 107.13 63.5301C108.297 67.1235 108.297 71.1375 108.297 79.1656V94.7307C108.297 102.759 108.297 106.773 107.13 110.366C106.096 113.546 104.407 116.473 102.17 118.957C99.6416 121.765 96.1655 123.772 89.2133 127.785C89.1537 127.82 89.0936 127.855 89.0341 127.889L75.7327 135.568C68.7803 139.582 65.304 141.589 61.6083 142.375C61.4564 142.407 61.3043 142.438 61.1519 142.467L61.1519 94.0288C61.1519 87.6997 57.6948 81.8761 52.1386 78.8455L11.6345 56.7522Z" fill="#000091"/>
<path d="M193.72 45.7333H211.035L234.771 108.456L258.507 45.7333H275.821L245.435 126H224.107L193.72 45.7333ZM289.019 58.1173C283.859 58.1173 279.501 53.76 279.501 48.6C279.501 43.44 283.859 39.0827 289.019 39.0827C294.179 39.0827 298.421 43.44 298.421 48.6C298.421 53.76 294.179 58.1173 289.019 58.1173ZM281.68 126V68.208H296.243V126H281.68ZM303.29 117.629L312.922 108.915C316.477 113.387 320.72 116.597 326.224 116.597C330.925 116.597 333.333 113.845 333.333 110.405C333.333 100.315 305.698 104.099 305.698 83.8027C305.698 73.5973 314.298 65.9147 326.338 65.9147C335.168 65.9147 343.194 70.1573 347.322 75.776L337.69 84.2613C334.709 80.592 330.925 77.6107 326.453 77.6107C321.866 77.6107 319.688 80.1333 319.688 83.1147C319.688 92.976 347.322 89.536 347.322 109.488C347.093 121.643 337.346 128.293 326.453 128.293C316.133 128.293 308.794 124.165 303.29 117.629ZM363.494 58.1173C358.334 58.1173 353.977 53.76 353.977 48.6C353.977 43.44 358.334 39.0827 363.494 39.0827C368.654 39.0827 372.897 43.44 372.897 48.6C372.897 53.76 368.654 58.1173 363.494 58.1173ZM356.155 126V68.208H370.718V126H356.155ZM411.115 65.9147C429.92 65.9147 442.763 79.7893 442.763 97.104C442.763 114.419 429.92 128.293 411.115 128.293C392.309 128.293 379.467 114.419 379.467 97.104C379.467 79.7893 392.309 65.9147 411.115 65.9147ZM411.344 114.533C420.632 114.533 427.627 107.08 427.627 97.104C427.627 87.0133 420.632 79.6747 411.344 79.6747C401.712 79.6747 394.603 87.0133 394.603 97.104C394.603 107.195 401.712 114.533 411.344 114.533Z" fill="#000091"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

@@ -7,7 +7,6 @@ import { Center } from '@/styled-system/jsx'
export const LoadingScreen = ({
delay = 500,
header = undefined,
footer = undefined,
layout = 'centered',
}: {
delay?: number
@@ -16,7 +15,7 @@ export const LoadingScreen = ({
return (
<DelayedRender delay={delay}>
<Screen layout={layout} header={header} footer={footer}>
<Screen layout={layout} header={header}>
<CenteredContent>
<Center>
<p>{t('loading')}</p>
+1 -1
View File
@@ -21,7 +21,7 @@ export const QueryAware = ({
}
if (status === 'pending') {
return <LoadingScreen header={undefined} footer={undefined} />
return <LoadingScreen header={undefined} />
}
return children
+1 -1
View File
@@ -27,7 +27,7 @@ export const SoundTester = () => {
return (
<>
<Button
variant="secondaryText"
invisible
onPress={() => {
audioRef?.current?.play()
setIsPlaying(true)
@@ -15,6 +15,6 @@ export const UserAware = ({ children }: { children: React.ReactNode }) => {
return isLoggedIn !== undefined ? (
children
) : (
<LoadingScreen header={false} footer={false} delay={1000} />
<LoadingScreen header={false} delay={1000} />
)
}
@@ -185,8 +185,8 @@ export const IntroSlider = () => {
<ButtonContainer>
<ButtonVerticalCenter>
<Button
variant="secondaryText"
square
invisible
aria-label={t('previous.label')}
tooltip={t('previous.tooltip')}
onPress={() => setSlideIndex(slideIndex - 1)}
@@ -221,8 +221,8 @@ export const IntroSlider = () => {
<ButtonContainer>
<ButtonVerticalCenter>
<Button
variant="secondaryText"
square
invisible
aria-label={t('next.label')}
tooltip={t('next.tooltip')}
onPress={() => setSlideIndex(slideIndex + 1)}
+7 -15
View File
@@ -10,6 +10,7 @@ import { JoinMeetingDialog } from '../components/JoinMeetingDialog'
import { ProConnectButton } from '@/components/ProConnectButton'
import { useCreateRoom } from '@/features/rooms'
import { usePersistentUserChoices } from '@livekit/components-react'
import { menuItemRecipe } from '@/primitives/menuItemRecipe'
import { RiAddLine, RiLink } from '@remixicon/react'
import { LaterMeetingDialog } from '@/features/home/components/LaterMeetingDialog'
import { IntroSlider } from '@/features/home/components/IntroSlider'
@@ -17,7 +18,6 @@ import { MoreLink } from '@/features/home/components/MoreLink'
import { ReactNode, useState } from 'react'
import { css } from '@/styled-system/css'
import { menuRecipe } from '@/primitives/menuRecipe.ts'
const Columns = ({ children }: { children?: ReactNode }) => {
return (
@@ -32,13 +32,8 @@ const Columns = ({ children }: { children?: ReactNode }) => {
justifyContent: 'normal',
padding: '0 1rem',
width: 'calc(100% - 2rem)',
'@media(prefers-reduced-motion: reduce)': {
opacity: 1,
},
'@media(prefers-reduced-motion: no-preference)': {
opacity: 0,
animation: '.5s ease-in fade 0s forwards',
},
opacity: 0,
animation: '.5s ease-in fade 0s forwards',
lg: {
flexDirection: 'row',
justifyContent: 'center',
@@ -178,9 +173,7 @@ export const Home = () => {
</Button>
<RACMenu>
<MenuItem
className={
menuRecipe({ icon: true, variant: 'light' }).item
}
className={menuItemRecipe({ icon: true })}
onAction={async () => {
const slug = generateRoomId()
createRoom({ slug, username }).then((data) =>
@@ -195,9 +188,7 @@ export const Home = () => {
{t('createMenu.instantOption')}
</MenuItem>
<MenuItem
className={
menuRecipe({ icon: true, variant: 'light' }).item
}
className={menuItemRecipe({ icon: true })}
onAction={() => {
const slug = generateRoomId()
createRoom({ slug, username }).then((data) =>
@@ -216,7 +207,8 @@ export const Home = () => {
)}
<DialogTrigger>
<Button
variant="secondary"
variant="primary"
outline
style={{
height: !isLoggedIn ? '56px' : undefined, // Temporary, Align with ProConnect Button fixed height
}}
@@ -3,7 +3,6 @@ export type ApiRoom = {
name: string
slug: string
is_public: boolean
is_administrable: boolean
livekit?: {
url: string
room: string
@@ -1,35 +0,0 @@
import { useMutation, UseMutationOptions } from '@tanstack/react-query'
import { fetchApi } from '@/api/fetchApi'
import { ApiError } from '@/api/ApiError'
import { ApiRoom } from './ApiRoom'
export enum RecordingMode {
Transcript = 'transcript',
ScreenRecording = 'screen_recording',
}
export interface StartRecordingParams {
id: string
mode?: RecordingMode
}
const startRecording = ({
id,
mode = RecordingMode.Transcript,
}: StartRecordingParams): Promise<ApiRoom> => {
return fetchApi(`rooms/${id}/start-recording/`, {
method: 'POST',
body: JSON.stringify({
mode: mode,
}),
})
}
export function useStartRecording(
options?: UseMutationOptions<ApiRoom, ApiError, StartRecordingParams>
) {
return useMutation<ApiRoom, ApiError, StartRecordingParams>({
mutationFn: startRecording,
onSuccess: options?.onSuccess,
})
}
@@ -1,23 +0,0 @@
import { useMutation, UseMutationOptions } from '@tanstack/react-query'
import { fetchApi } from '@/api/fetchApi'
import { ApiError } from '@/api/ApiError'
import { ApiRoom } from './ApiRoom'
export interface StopRecordingParams {
id: string
}
const stopRecording = ({ id }: StopRecordingParams): Promise<ApiRoom> => {
return fetchApi(`rooms/${id}/stop-recording/`, {
method: 'POST',
})
}
export function useStopRecording(
options?: UseMutationOptions<ApiRoom, ApiError, StopRecordingParams>
) {
return useMutation<ApiRoom, ApiError, StopRecordingParams>({
mutationFn: stopRecording,
onSuccess: options?.onSuccess,
})
}
@@ -15,7 +15,6 @@ import { InviteDialog } from './InviteDialog'
import { VideoConference } from '../livekit/prefabs/VideoConference'
import posthog from 'posthog-js'
import { css } from '@/styled-system/css'
export const Conference = ({
roomId,
@@ -99,7 +98,7 @@ export const Conference = ({
return (
<QueryAware status={isFetchError ? createStatus : fetchStatus}>
<Screen header={false} footer={false}>
<Screen header={false}>
<LiveKitRoom
room={room}
serverUrl={data?.livekit?.url}
@@ -108,9 +107,6 @@ export const Conference = ({
audio={userConfig.audioEnabled}
video={userConfig.videoEnabled}
connectOptions={connectOptions}
className={css({
backgroundColor: 'primaryDark.50 !important',
})}
>
<VideoConference />
{showInviteDialog && (
@@ -49,6 +49,8 @@ export const InviteDialog = ({
}
}, [isCopied])
const [isHovered, setIsHovered] = useState(false)
return (
<StyledRACDialog {...dialogProps}>
{({ close }) => (
@@ -64,7 +66,6 @@ export const InviteDialog = ({
<Div position="absolute" top="5" right="5">
<Button
invisible
variant="tertiaryText"
size="xs"
onPress={() => {
dialogProps.onClose?.()
@@ -77,24 +78,45 @@ export const InviteDialog = ({
</Div>
<P>{t('shareDialog.description')}</P>
<Button
variant={isCopied ? 'success' : 'tertiary'}
variant={isCopied ? 'success' : 'primary'}
size="sm"
fullWidth
aria-label={t('shareDialog.copy')}
style={{
justifyContent: 'start',
}}
onPress={() => {
navigator.clipboard.writeText(roomUrl)
setIsCopied(true)
}}
onHoverChange={setIsHovered}
data-attr="share-dialog-copy"
>
{isCopied ? (
<>
<RiCheckLine size={24} style={{ marginRight: '8px' }} />
<RiCheckLine size={18} style={{ marginRight: '8px' }} />
{t('shareDialog.copied')}
</>
) : (
<>
<RiFileCopyLine size={24} style={{ marginRight: '8px' }} />
{t('shareDialog.copyButton')}
<RiFileCopyLine
size={18}
style={{ marginRight: '8px', minWidth: '18px' }}
/>
{isHovered ? (
t('shareDialog.copy')
) : (
<div
style={{
textOverflow: 'ellipsis',
overflow: 'hidden',
userSelect: 'none',
textWrap: 'nowrap',
}}
>
{roomUrl.replace(/^https?:\/\//, '')}
</div>
)}
</>
)}
</Button>
@@ -13,7 +13,7 @@ export const Join = ({
const { user } = useUser()
return (
<Screen layout="centered" footer={false}>
<Screen layout="centered">
<CenteredContent title={t('join.heading')}>
<PreJoin
persistUserChoices
@@ -43,7 +43,7 @@ const ratingButtonRecipe = cva({
variants: {
selected: {
true: {
backgroundColor: 'primary.800',
backgroundColor: '#1d4ed8',
color: 'white',
},
false: {
@@ -151,6 +151,7 @@ export const Effects = () => {
<HStack>
<ToggleButton
size={'sm'}
legacyStyle
aria-label={tooltipLabel(BlurRadius.LIGHT)}
tooltip={tooltipLabel(BlurRadius.LIGHT)}
isDisabled={processorPending}
@@ -161,6 +162,7 @@ export const Effects = () => {
</ToggleButton>
<ToggleButton
size={'sm'}
legacyStyle
aria-label={tooltipLabel(BlurRadius.NORMAL)}
tooltip={tooltipLabel(BlurRadius.NORMAL)}
isDisabled={processorPending}
@@ -10,7 +10,7 @@ const StyledParticipantPlaceHolder = styled('div', {
base: {
width: '100%',
height: '100%',
backgroundColor: 'primaryDark.100',
backgroundColor: '#3d4043', // fixme - copied from gmeet
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
@@ -68,7 +68,6 @@ const StyledSidePanel = ({
>
<Button
invisible
variant="tertiaryText"
size="xs"
onPress={onClose}
aria-label={closeButtonTooltip}
@@ -107,7 +107,6 @@ export const ChatInput = ({
<Button
square
invisible
variant="tertiaryText"
size="sm"
onPress={handleSubmit}
isDisabled={isDisabled}
@@ -23,7 +23,7 @@ export const ChatToggle = () => {
>
<ToggleButton
square
variant="primaryTextDark"
legacyStyle
aria-label={t(tooltipLabel)}
tooltip={t(tooltipLabel)}
isSelected={isChatOpen}
@@ -24,7 +24,7 @@ export const HandToggle = () => {
>
<ToggleButton
square
variant="primaryDark"
legacyStyle
aria-label={t(tooltipLabel)}
tooltip={t(tooltipLabel)}
isSelected={isHandRaised}
@@ -16,7 +16,7 @@ export const OptionsButton = () => {
<Menu>
<Button
square
variant="primaryDark"
legacyStyle
aria-label={t('options.buttonLabel')}
tooltip={t('options.buttonLabel')}
>
@@ -1,3 +1,4 @@
import { menuItemRecipe } from '@/primitives/menuItemRecipe'
import {
RiAccountBoxLine,
RiMegaphoneLine,
@@ -9,8 +10,6 @@ import { Dispatch, SetStateAction } from 'react'
import { DialogState } from './OptionsButton'
import { Separator } from '@/primitives/Separator'
import { useSidePanel } from '../../../hooks/useSidePanel'
import { menuRecipe } from '@/primitives/menuRecipe.ts'
import { TranscriptMenuItem } from './TranscriptMenuItem'
// @todo try refactoring it to use MenuList component
export const OptionsMenuItems = ({
@@ -30,25 +29,24 @@ export const OptionsMenuItems = ({
<Section>
<MenuItem
onAction={() => toggleEffects()}
className={menuRecipe({ icon: true }).item}
className={menuItemRecipe({ icon: true })}
>
<RiAccountBoxLine size={20} />
{t('effects')}
</MenuItem>
<TranscriptMenuItem />
</Section>
<Separator />
<Section>
<MenuItem
href="https://grist.incubateur.net/o/docs/forms/1YrfNP1QSSy8p2gCxMFnSf/4"
target="_blank"
className={menuRecipe({ icon: true }).item}
className={menuItemRecipe({ icon: true })}
>
<RiMegaphoneLine size={20} />
{t('feedbacks')}
</MenuItem>
<MenuItem
className={menuRecipe({ icon: true }).item}
className={menuItemRecipe({ icon: true })}
onAction={() => onOpenDialog('settings')}
>
<RiSettings3Line size={20} />
@@ -1,71 +0,0 @@
import { RiRecordCircleLine, RiStopCircleLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { menuRecipe } from '@/primitives/menuRecipe'
import { MenuItem } from 'react-aria-components'
import {
RecordingMode,
useStartRecording,
} from '@/features/rooms/api/startRecording'
import { useStopRecording } from '@/features/rooms/api/stopRecording'
import { useRoomContext } from '@livekit/components-react'
import { useRoomData } from '@/features/rooms/livekit/hooks/useRoomData'
import { useConfig } from '@/api/useConfig'
export const TranscriptMenuItem = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
const apiRoomData = useRoomData()
const { mutateAsync: startRecordingRoom } = useStartRecording()
const { mutateAsync: stopRecordingRoom } = useStopRecording()
const { data } = useConfig()
const room = useRoomContext()
const handleTranscript = async () => {
const roomId = apiRoomData?.livekit?.room
if (!roomId) {
console.warn('No room ID found')
return
}
try {
if (room.isRecording) {
await stopRecordingRoom({ id: roomId })
} else {
await startRecordingRoom({ id: roomId, mode: RecordingMode.Transcript })
}
} catch (error) {
console.error('Failed to handle transcript:', error)
}
}
if (
!data?.recording?.is_enabled ||
!data?.recording?.available_modes?.includes(RecordingMode.Transcript) ||
!apiRoomData?.is_administrable
) {
return
}
return (
<MenuItem
className={menuRecipe({ icon: true }).item}
onAction={async () => await handleTranscript()}
>
{room.isRecording ? (
<>
<RiRecordCircleLine size={20} />
{t('transcript.stop')}
</>
) : (
<>
<RiStopCircleLine size={20} />
{t('transcript.start')}
</>
)}
</MenuItem>
)
}
@@ -69,7 +69,7 @@ export const HandRaisedListItem = ({
</HStack>
<Button
square
variant="greyscale"
invisible
size="sm"
onPress={() => lowerHandParticipant(participant)}
tooltip={t('participants.lowerParticipantHand', { name })}
@@ -63,7 +63,7 @@ const MicIndicator = ({ participant }: MicIndicatorProps) => {
<>
<Button
square
variant="greyscale"
invisible
size="sm"
tooltip={
isLocal(participant)
@@ -29,7 +29,7 @@ export const ParticipantsToggle = () => {
>
<ToggleButton
square
variant="primaryTextDark"
legacyStyle
aria-label={t(tooltipLabel)}
tooltip={t(tooltipLabel)}
isSelected={isParticipantsOpen}
@@ -26,7 +26,7 @@ export const ScreenShareToggle = (
<ToggleButton
isSelected={enabled}
square
variant="primaryDark"
legacyStyle
tooltip={t(tooltipLabel)}
onPress={(e) =>
buttonProps.onClick?.(
@@ -4,6 +4,7 @@ import {
useTrackToggle,
UseTrackToggleProps,
} from '@livekit/components-react'
import { HStack } from '@/styled-system/jsx'
import { Button, Menu, MenuList } from '@/primitives'
import {
RemixiconComponentType,
@@ -18,7 +19,6 @@ import { Track } from 'livekit-client'
import { Shortcut } from '@/features/shortcuts/types'
import { ToggleDevice } from '@/features/rooms/livekit/components/controls/ToggleDevice.tsx'
import { css } from '@/styled-system/css'
export type ToggleSource = Exclude<
Track.Source,
@@ -86,12 +86,7 @@ export const SelectToggleDevice = <T extends ToggleSource>({
const selectLabel = t('choose', { keyPrefix: `join.${config.kind}` })
return (
<div
className={css({
display: 'flex',
gap: '1px',
})}
>
<HStack gap={0}>
<ToggleDevice {...trackProps} config={config} />
<Menu>
<Button
@@ -99,7 +94,6 @@ export const SelectToggleDevice = <T extends ToggleSource>({
aria-label={selectLabel}
groupPosition="right"
square
variant={trackProps.enabled ? 'primaryDark' : 'error2'}
>
<RiArrowDownSLine />
</Button>
@@ -115,6 +109,6 @@ export const SelectToggleDevice = <T extends ToggleSource>({
}}
/>
</Menu>
</div>
</HStack>
)
}
@@ -1,41 +0,0 @@
import { ToggleButton } from '@/primitives'
import { RiQuestionLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { Crisp } from 'crisp-sdk-web'
import { useEffect, useState } from 'react'
export const SupportToggle = () => {
const { t } = useTranslation('rooms', { keyPrefix: 'controls' })
const [isOpened, setIsOpened] = useState($crisp.is('chat:opened'))
useEffect(() => {
if (!Crisp) {
return
}
Crisp.chat.onChatOpened(() => {
setIsOpened(true)
})
Crisp.chat.onChatClosed(() => {
setIsOpened(false)
})
return () => {
Crisp.chat.offChatOpened()
Crisp.chat.offChatClosed()
}
}, [])
return (
<ToggleButton
square
variant="primaryTextDark"
tooltip={t('support')}
aria-label={t('support')}
isSelected={isOpened}
onPress={() => (isOpened ? Crisp.chat.close() : Crisp.chat.open())}
data-attr="controls-support"
>
<RiQuestionLine />
</ToggleButton>
)
}
@@ -57,9 +57,9 @@ export const ToggleDevice = ({
return (
<ToggleButton
isSelected={!enabled}
variant={enabled ? 'primaryDark' : 'error2'}
shySelected
isSelected={enabled}
variant={enabled ? undefined : 'danger'}
toggledStyles={false}
onPress={() => toggle()}
aria-label={toggleLabel}
tooltip={toggleLabel}
@@ -3,9 +3,13 @@ import * as React from 'react'
import { supportsScreenSharing } from '@livekit/components-core'
import { usePersistentUserChoices } from '@livekit/components-react'
import {
useMaybeLayoutContext,
usePersistentUserChoices,
} from '@livekit/components-react'
import { StartMediaButton } from '../components/controls/StartMediaButton'
import { useMediaQuery } from '../hooks/useMediaQuery'
import { OptionsButton } from '../components/controls/Options/OptionsButton'
import { ParticipantsToggle } from '../components/controls/Participants/ParticipantsToggle'
import { ChatToggle } from '../components/controls/ChatToggle'
@@ -14,7 +18,6 @@ import { SelectToggleDevice } from '../components/controls/SelectToggleDevice'
import { LeaveButton } from '../components/controls/LeaveButton'
import { ScreenShareToggle } from '../components/controls/ScreenShareToggle'
import { css } from '@/styled-system/css'
import { SupportToggle } from '@/features/rooms/livekit/components/controls/SupportToggle.tsx'
/** @public */
export type ControlBarControls = {
@@ -57,9 +60,25 @@ export interface ControlBarProps extends React.HTMLAttributes<HTMLDivElement> {
* @public
*/
export function ControlBar({
variation,
saveUserChoices = true,
onDeviceError,
}: ControlBarProps) {
const [isChatOpen, setIsChatOpen] = React.useState(false)
const layoutContext = useMaybeLayoutContext()
React.useEffect(() => {
if (layoutContext?.widget.state?.showChat !== undefined) {
setIsChatOpen(layoutContext?.widget.state?.showChat)
}
}, [layoutContext?.widget.state?.showChat])
const isTooLittleSpace = useMediaQuery(
`(max-width: ${isChatOpen ? 1000 : 760}px)`
)
const defaultVariation = isTooLittleSpace ? 'minimal' : 'verbose'
variation ??= defaultVariation
const browserSupportsScreenSharing = supportsScreenSharing()
const {
@@ -84,80 +103,54 @@ export function ControlBar({
return (
<div
className={css({
width: '100vw',
display: 'flex',
gap: '.5rem',
alignItems: 'center',
justifyContent: 'center',
padding: '.75rem',
borderTop: '1px solid var(--lk-border-color)',
maxHeight: 'var(--lk-control-bar-height)',
height: '80px',
position: 'absolute',
padding: '1.125rem',
backgroundColor: '#d1d5db',
bottom: 0,
left: 0,
right: 0,
})}
>
<div
className={css({
display: 'flex',
justifyContent: 'flex-start',
flex: '1 1 33%',
alignItems: 'center',
gap: '0.5rem',
marginLeft: '0.5rem',
})}
></div>
<div
className={css({
flex: '1 1 33%',
alignItems: 'center',
justifyContent: 'center',
display: 'flex',
gap: '0.65rem',
})}
>
<SelectToggleDevice
source={Track.Source.Microphone}
onChange={microphoneOnChange}
<SelectToggleDevice
source={Track.Source.Microphone}
onChange={microphoneOnChange}
onDeviceError={(error) =>
onDeviceError?.({ source: Track.Source.Microphone, error })
}
onActiveDeviceChange={(deviceId) =>
saveAudioInputDeviceId(deviceId ?? '')
}
/>
<SelectToggleDevice
source={Track.Source.Camera}
onChange={cameraOnChange}
onDeviceError={(error) =>
onDeviceError?.({ source: Track.Source.Camera, error })
}
onActiveDeviceChange={(deviceId) =>
saveVideoInputDeviceId(deviceId ?? '')
}
/>
{browserSupportsScreenSharing && (
<ScreenShareToggle
onDeviceError={(error) =>
onDeviceError?.({ source: Track.Source.Microphone, error })
}
onActiveDeviceChange={(deviceId) =>
saveAudioInputDeviceId(deviceId ?? '')
onDeviceError?.({ source: Track.Source.ScreenShare, error })
}
/>
<SelectToggleDevice
source={Track.Source.Camera}
onChange={cameraOnChange}
onDeviceError={(error) =>
onDeviceError?.({ source: Track.Source.Camera, error })
}
onActiveDeviceChange={(deviceId) =>
saveVideoInputDeviceId(deviceId ?? '')
}
/>
{browserSupportsScreenSharing && (
<ScreenShareToggle
onDeviceError={(error) =>
onDeviceError?.({ source: Track.Source.ScreenShare, error })
}
/>
)}
<HandToggle />
<OptionsButton />
<LeaveButton />
<StartMediaButton />
</div>
<div
className={css({
display: 'flex',
justifyContent: 'flex-end',
flex: '1 1 33%',
alignItems: 'center',
gap: '0.5rem',
paddingRight: '0.25rem',
})}
>
<ChatToggle />
<ParticipantsToggle />
<SupportToggle />
</div>
)}
<HandToggle />
<ChatToggle />
<ParticipantsToggle />
<OptionsButton />
<LeaveButton />
<StartMediaButton />
</div>
)
}
@@ -23,12 +23,16 @@ export const FeedbackRoute = () => {
const { t } = useTranslation('rooms')
const [, setLocation] = useLocation()
return (
<Screen layout="centered" footer={false}>
<Screen layout="centered">
<Center>
<VStack>
<Heading>{t('feedback.heading')}</Heading>
<HStack>
<Button variant="secondary" onPress={() => window.history.back()}>
<Button
outline
variant="primary"
onPress={() => window.history.back()}
>
{t('feedback.back')}
</Button>
<Button variant="primary" onPress={() => setLocation('/')}>
@@ -10,7 +10,7 @@ export const SettingsButton = () => {
<DialogTrigger>
<Button
square
variant="secondaryText"
invisible
aria-label={t('settingsButtonLabel')}
tooltip={t('settingsButtonLabel')}
>
@@ -68,7 +68,7 @@ export const AccountTab = ({ id, onOpenChange }: AccountTabProps) => {
marginLeft: 'auto',
})}
>
<Button variant="secondary" onPress={handleOnCancel}>
<Button onPress={handleOnCancel}>
{t('cancel', { ns: 'global' })}
</Button>
<Button variant={'primary'} onPress={handleOnSubmit}>
-272
View File
@@ -1,272 +0,0 @@
import { styled } from '@/styled-system/jsx'
import { css } from '@/styled-system/css'
import { A } from '@/primitives'
import { useTranslation } from 'react-i18next'
const StyledLi = styled('li', {
base: {},
variants: {
divider: {
true: {
_after: {
content: '""',
display: 'inline-block',
marginX: '.75rem',
verticalAlign: 'middle',
boxShadow: 'inset 0 0 0 1px #ddd',
height: '1rem',
width: '1px',
},
},
},
},
})
const InnerContainer = styled('div', {
base: {
display: 'flex',
flexDirection: 'column',
alignItems: 'start',
margin: 'auto',
maxWidth: '1200px',
paddingX: { base: '0.5rem', xs: '1rem', sm: '2rem' },
},
})
const MainLinkList = styled('ul', {
base: {
display: 'flex',
gap: '0.5rem 1rem',
flexWrap: 'wrap',
flexBasis: { base: '100%', md: '50%' },
},
})
const FirstRow = styled('div', {
base: {
display: 'flex',
gap: '2rem',
flexWrap: { base: 'wrap', md: 'nowrap' },
justifyContent: 'space-between',
width: '100%',
alignItems: 'flex-start',
marginBottom: '1.5rem',
},
})
const SecondRow = styled('ul', {
base: {
display: 'flex',
borderTop: '1px solid rgb(217 217 217)',
paddingTop: '0.5rem',
width: '100%',
flexWrap: 'wrap',
alignItems: 'center',
},
})
const ThirdRow = styled('p', {
base: {
fontSize: '0.75rem',
color: 'rgb(77 77 77)',
fontFamily: 'Marianne',
textWrap: 'wrap',
lineHeight: '1rem',
marginTop: { base: '1rem', xs: '0.5rem' },
},
})
const Marianne = () => {
return (
<div
className={css({
_before: {
content: '""',
display: 'block',
backgroundRepeat: 'no-repeat',
backgroundSize: 'contain',
backgroundImage: 'url(/assets/marianne.svg)',
height: '1.25rem',
marginBottom: '.2rem',
width: '3rem',
},
_after: {
content: '""',
display: 'block',
backgroundImage: 'url(/assets/devise.svg)',
backgroundRepeat: 'no-repeat',
backgroundSize: 'contain',
height: '2.313rem',
marginTop: '.2rem',
width: '3.25rem',
},
})}
>
<p
className={css({
letterSpacing: '-.01em',
textTransform: 'uppercase',
fontWeight: '600',
fontFamily: 'Marianne',
fontSize: '1.25rem',
lineHeight: '1.75rem',
})}
>
gouvernement
</p>
</div>
)
}
export const Footer = () => {
const { t } = useTranslation('global', { keyPrefix: 'footer' })
return (
<footer
className={css({
borderTop: '2px solid rgb(0 0 145)',
paddingY: '2rem',
marginTop: { base: '50px', sm: '100px' },
})}
>
<InnerContainer>
<FirstRow>
<div
className={css({
display: 'flex',
paddingBottom: '1.5rem',
paddingX: '1.5rem',
alignItems: 'center',
gap: '1.5rem',
})}
>
<Marianne />
<span
className={css({
height: '80px',
backgroundColor: 'rgb(77 77 77)',
width: '1px',
display: { base: 'none', sm: 'block' },
})}
/>
<p
className={css({
display: 'none',
fontWeight: '700',
fontFamily: 'Marianne',
sm: {
display: 'block',
fontSize: '1rem',
lineHeight: '1.5rem',
},
})}
>
Direction
<br />
interministérielle
<br />
du numérique
</p>
</div>
<MainLinkList>
<li>
<A
externalIcon
underline={false}
footer="important"
href="https://legifrance.gouv.fr"
aria-label={
t('links.legifrance') + ' - ' + t('links.ariaLabel')
}
>
{t('links.legifrance')}
</A>
</li>
<li>
<A
externalIcon
underline={false}
footer="important"
href="https://info.gouv.fr"
aria-label={t('links.infogouv') + ' - ' + t('links.ariaLabel')}
>
{t('links.infogouv')}
</A>
</li>
<li>
<A
externalIcon
underline={false}
footer="important"
href="https://www.service-public.fr/"
aria-label={
t('links.servicepublic') + ' - ' + t('links.ariaLabel')
}
>
{t('links.servicepublic')}
</A>
</li>
<li>
<A
externalIcon
underline={false}
footer="important"
href="https://data.gouv.fr"
aria-label={t('links.datagouv') + ' - ' + t('links.ariaLabel')}
>
{t('links.datagouv')}
</A>
</li>
</MainLinkList>
</FirstRow>
<SecondRow>
<StyledLi divider>
<A
externalIcon
underline={false}
footer="minor"
href="https://docs.numerique.gouv.fr/docs/f88a2eb0-7ce7-4016-b6ee-9f1fd1771951/"
aria-label={t('links.legalsTerms') + ' - ' + t('links.ariaLabel')}
>
{t('links.legalsTerms')}
</A>
</StyledLi>
<StyledLi divider>
<A
externalIcon
underline={false}
footer="minor"
href="https://docs.numerique.gouv.fr/docs/168d7e8e-3f09-462d-8bbc-ea95dedd3889/"
aria-label={t('links.data') + ' - ' + t('links.ariaLabel')}
>
{t('links.data')}
</A>
</StyledLi>
<StyledLi>
<A
externalIcon
underline={false}
footer="minor"
href="https://docs.numerique.gouv.fr/docs/94bd1e3b-a44d-4cf5-b7ee-708a5386a111/"
aria-label={
t('links.accessibility') + ' - ' + t('links.ariaLabel')
}
>
{t('links.accessibility')}
</A>
</StyledLi>
</SecondRow>
<ThirdRow>
{t('mentions')}{' '}
<A
externalIcon
footer="minor"
href="https://github.com/etalab/licence-ouverte/blob/master/LO.md"
>
{t('license')}
</A>
</ThirdRow>
</InnerContainer>
</footer>
)
}
+32 -119
View File
@@ -1,98 +1,18 @@
import { Link } from 'wouter'
import { css } from '@/styled-system/css'
import { HStack, Stack } from '@/styled-system/jsx'
import { Stack } from '@/styled-system/jsx'
import { useTranslation } from 'react-i18next'
import { Button } from '@/primitives'
import { Text, Button } from '@/primitives'
import { SettingsButton } from '@/features/settings'
import { logoutUrl, useUser } from '@/features/auth'
import { useMatchesRoute } from '@/navigation/useMatchesRoute'
import { FeedbackBanner } from '@/components/FeedbackBanner'
import { FeedbackBanner } from '@/components/FeedbackBanner.tsx'
import { Menu } from '@/primitives/Menu'
import { MenuList } from '@/primitives/MenuList'
import { ProConnectButton } from '@/components/ProConnectButton'
import { terminateAnalyticsSession } from '@/features/analytics/hooks/useAnalytics'
import { terminateSupportSession } from '@/features/support/hooks/useSupport'
import LogoAsset from '@/assets/logo.svg'
const Marianne = () => {
return (
<div
className={css({
_before: {
content: '""',
display: 'block',
backgroundImage: 'url(/assets/marianne.svg)',
backgroundPosition: '0 -0.046875rem, 0 0, 0 0',
backgroundSize: '2.0625rem 0.84375rem, 2.0625rem 0.75rem, 0',
height: '0.75rem',
marginBottom: '0.1rem',
width: '2.0625rem',
},
_after: {
content: '""',
display: 'block',
backgroundImage: 'url(/assets/devise.svg)',
backgroundRepeat: 'no-repeat',
backgroundSize: 'contain',
width: '2.5rem',
height: '1.7857142857142858rem',
marginTop: '0.25rem',
},
})}
>
<p
className={css({
letterSpacing: '-.01em',
textTransform: 'uppercase',
fontWeight: '600',
fontFamily: 'Marianne',
fontSize: '0.7875rem !important',
})}
>
gouvernement
</p>
</div>
)
}
const BetaBadge = () => (
<span
className={css({
content: '"Beta"',
display: 'block',
letterSpacing: '-0.02rem',
padding: '0 0.25rem',
backgroundColor: '#E8EDFF',
color: '#0063CB',
fontSize: '12px',
fontWeight: 500,
margin: '0 0 0.9375rem 0.3125rem',
lineHeight: '1rem',
borderRadius: '4px',
width: 'fit-content',
height: 'fit-content',
marginTop: { base: '10px', sm: '5px' },
})}
>
Beta
</span>
)
const Logo = () => {
const { t } = useTranslation()
return (
<img
src={LogoAsset}
alt={t('app')}
className={css({
maxHeight: { base: '30px', sm: '40px' },
marginTop: { base: '10px', sm: '5px' },
})}
/>
)
}
export const Header = () => {
const { t } = useTranslation()
const isHome = useMatchesRoute('home')
@@ -104,43 +24,37 @@ export const Header = () => {
<FeedbackBanner />
<div
className={css({
paddingBottom: 1,
paddingY: 1,
paddingX: 1,
paddingTop: 0.25,
flexShrink: 0,
})}
>
<HStack gap={0} justify="space-between" alignItems="center">
<div
className={css({
display: 'flex',
rowGap: 0,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
})}
>
<header>
<Stack gap={2.25} direction="row" align="center">
<Link
className={css({
display: 'flex',
flexDirection: { base: 'column', sm: 'row' },
alignItems: 'start',
gap: { base: '0', sm: '2rem' },
padding: { base: '0.5rem', sm: '1rem' },
_hover: {
backgroundColor: 'greyscale.100',
borderRadius: '4px',
},
})}
onClick={(event) => {
if (
isRoom &&
!window.confirm(t('leaveRoomPrompt', { ns: 'rooms' }))
) {
event.preventDefault()
}
}}
to="/"
>
<Marianne />
<HStack gap={0}>
<Logo />
<BetaBadge />
</HStack>
</Link>
<Text bold variant="h1" margin={false}>
<Link
onClick={(event) => {
if (
isRoom &&
!window.confirm(t('leaveRoomPrompt', { ns: 'rooms' }))
) {
event.preventDefault()
}
}}
to="/"
>
{t('app')}
</Link>
</Text>
</Stack>
</header>
<nav>
@@ -152,7 +66,7 @@ export const Header = () => {
<Menu>
<Button
size="sm"
variant="secondaryText"
invisible
tooltip={t('loggedInUserTooltip')}
tooltipType="delayed"
>
@@ -161,15 +75,14 @@ export const Header = () => {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
maxWidth: '350px',
display: { base: 'none', xsm: 'block' },
display: 'block',
maxWidth: { base: '80px', xsm: '350px' },
})}
>
{user?.full_name || user?.email}
</span>
</Button>
<MenuList
variant={'light'}
items={[{ value: 'logout', label: t('logout') }]}
onAction={(value) => {
if (value === 'logout') {
@@ -184,7 +97,7 @@ export const Header = () => {
<SettingsButton />
</Stack>
</nav>
</HStack>
</div>
</div>
</>
)
+16 -24
View File
@@ -3,7 +3,6 @@ import { css } from '@/styled-system/css'
import { Header } from './Header'
import { layoutStore } from '@/stores/layout'
import { useSnapshot } from 'valtio'
import { Footer } from '@/layout/Footer'
export type Layout = 'fullpage' | 'centered'
@@ -16,35 +15,28 @@ export type Layout = 'fullpage' | 'centered'
export const Layout = ({ children }: { children: ReactNode }) => {
const layoutSnap = useSnapshot(layoutStore)
const showHeader = layoutSnap.showHeader
const showFooter = layoutSnap.showFooter
return (
<>
<div
<div
className={css({
height: '100%',
display: 'flex',
flexDirection: 'column',
backgroundColor: 'white',
color: 'default.text',
})}
>
{showHeader && <Header />}
<main
className={css({
flexGrow: 1,
overflow: 'auto',
display: 'flex',
flexDirection: 'column',
backgroundColor: 'white',
color: 'default.text',
flex: '1',
})}
style={{
height: !showFooter ? '100%' : undefined,
}}
>
{showHeader && <Header />}
<main
className={css({
flexGrow: 1,
overflow: 'auto',
display: 'flex',
flexDirection: 'column',
})}
>
{children}
</main>
</div>
{showFooter && <Footer />}
</>
{children}
</main>
</div>
)
}
+1 -6
View File
@@ -13,23 +13,18 @@ export type ScreenProps = {
* True by default. Pass undefined to render the screen without modifying current header visibility
*/
header?: boolean
footer?: boolean
children: React.ReactNode
}
export const Screen = ({
layout = 'fullpage',
header = true,
footer = true,
children,
}: ScreenProps) => {
useEffect(() => {
if (header !== undefined) {
layoutStore.showHeader = header
}
if (footer !== undefined) {
layoutStore.showFooter = footer
}
}, [header, footer])
}, [header])
return layout === 'centered' ? <Centered>{children}</Centered> : children
}
+1 -15
View File
@@ -24,19 +24,5 @@
"notFound": {
"heading": ""
},
"submit": "OK",
"footer": {
"links": {
"legifrance": "",
"infogouv": "",
"servicepublic": "",
"datagouv": "",
"legalsTerms": "",
"data": "",
"accessibility": "",
"ariaLabel": ""
},
"mentions": "",
"license": ""
}
"submit": "OK"
}
+3 -7
View File
@@ -70,20 +70,16 @@
"participants": {
"open": "",
"closed": ""
},
"support": ""
}
},
"options": {
"buttonLabel": "",
"items": {
"feedbacks": "",
"support": "",
"settings": "",
"username": "",
"effects": "",
"transcript": {
"start": "",
"stop": ""
}
"effects": ""
}
},
"effects": {
+1 -15
View File
@@ -24,19 +24,5 @@
"notFound": {
"heading": "Page not found"
},
"submit": "OK",
"footer": {
"links": {
"legifrance": "legifrance.gouv.fr",
"infogouv": "info.gouv.fr",
"servicepublic": "service-public.fr",
"datagouv": "data.gouv.fr",
"legalsTerms": "Legal Notice",
"data": "Personal Data and Cookies",
"accessibility": "Accessibility: audit in progress",
"ariaLabel": "new window"
},
"mentions": "Unless otherwise stated, the contents of this site are available under",
"license": "etalab 2.0 license"
}
"submit": "OK"
}
+3 -8
View File
@@ -29,7 +29,6 @@
"leaveRoomPrompt": "This will make you leave the meeting.",
"shareDialog": {
"copy": "Copy the meeting link",
"copyButton": "Copy link",
"copied": "Link copied to clipboard",
"heading": "Your meeting is ready",
"description": "Share this link with people you want to invite to the meeting.",
@@ -69,20 +68,16 @@
"participants": {
"open": "Hide everyone",
"closed": "See everyone"
},
"support": "Support"
}
},
"options": {
"buttonLabel": "More Options",
"items": {
"feedbacks": "Give us feedbacks",
"support": "Get Help on Tchap",
"settings": "Settings",
"username": "Update Your Name",
"effects": "Apply effects",
"transcript": {
"start": "Start meeting transcription",
"stop": "Stop ongoing transcription"
}
"effects": "Apply effects"
}
},
"effects": {
+1 -15
View File
@@ -24,19 +24,5 @@
"notFound": {
"heading": "Page introuvable"
},
"submit": "OK",
"footer": {
"links": {
"legifrance": "legifrance.gouv.fr",
"infogouv": "info.gouv.fr",
"servicepublic": "service-public.fr",
"datagouv": "data.gouv.fr",
"legalsTerms": "Mentions légales",
"data": "Données personnelles et cookie",
"accessibility": "Accessibilités : audit en cours",
"ariaLabel": "nouvelle fenêtre"
},
"mentions": "Sauf mention contraire, les contenus de ce site sont disponibles sous",
"license": "licence etalab 2.0"
}
"submit": "OK"
}
+4 -9
View File
@@ -29,7 +29,6 @@
"leaveRoomPrompt": "Revenir à l'accueil vous fera quitter la réunion.",
"shareDialog": {
"copy": "Copier le lien de la réunion",
"copyButton": "Copier le lien",
"copied": "Lien copié dans le presse-papiers",
"heading": "Votre réunion est prête",
"description": "Partagez ce lien avec les personnes que vous souhaitez inviter à la réunion.",
@@ -69,20 +68,16 @@
"participants": {
"open": "Masquer les participants",
"closed": "Afficher les participants"
},
"support": "Support"
}
},
"options": {
"buttonLabel": "Plus d'options",
"items": {
"feedbacks": "Partager votre avis",
"support": "Obtenir de l'aide sur Tchap",
"settings": "Paramètres",
"username": "Choisir votre nom",
"effects": "Appliquer des effets",
"transcript": {
"start": "Démarrer la transcription",
"stop": "Arrêter la transcription en cours"
}
"effects": "Appliquer des effets"
}
},
"effects": {
@@ -128,7 +123,7 @@
"skip": "Passer"
},
"confirmationMessage": {
"heading": "Merci pour votre retour",
"heading": "Merci pour votre submission",
"body": "Notre équipe produit prend le temps d'analyser attentivement vos réponses. Nous reviendrons vers vous dans les plus brefs délais."
},
"participants": {
+2 -43
View File
@@ -21,36 +21,6 @@ const link = cva({
textStyle: 'sm',
},
},
externalIcon: {
true: {
_after: {
content: 'url(/assets/link-grey.svg)',
verticalAlign: 'middle',
paddingLeft: '.25rem',
},
},
},
underline: {
false: {
textDecoration: 'none',
},
},
footer: {
important: {
fontSize: '0.8rem',
lineHeight: '1rem',
fontWeight: '700',
fontFamily: 'Marianne',
textWrap: 'nowrap',
},
minor: {
fontSize: '0.75rem',
color: 'rgb(77 77 77)',
fontFamily: 'Marianne',
textWrap: 'nowrap',
lineHeight: '1rem',
},
},
},
})
@@ -59,17 +29,6 @@ export type AProps = LinkProps & RecipeVariantProps<typeof link>
/**
* anchor component styled with underline. Used mostly for external links. Use Link for internal links
*/
export const A = ({
size,
externalIcon,
underline,
footer,
...props
}: AProps) => {
return (
<Link
{...props}
className={link({ size, externalIcon, underline, footer })}
/>
)
export const A = ({ size, ...props }: AProps) => {
return <Link {...props} className={link({ size })} />
}
-1
View File
@@ -112,7 +112,6 @@ export const Dialog = ({
{!isAlert && (
<Div position="absolute" top="5" right="5">
<Button
variant="tertiaryText"
invisible
size="xs"
onPress={() => close()}
+1 -1
View File
@@ -51,7 +51,7 @@ export const Form = ({
{submitLabel}
</Button>
{!!onCancel && (
<Button variant="secondary" onPress={() => onCancel()}>
<Button variant="primary" outline onPress={() => onCancel()}>
{t('cancel')}
</Button>
)}
+3 -8
View File
@@ -1,7 +1,6 @@
import { ReactNode } from 'react'
import { Menu, MenuProps, MenuItem } from 'react-aria-components'
import { menuRecipe } from '@/primitives/menuRecipe.ts'
import type { RecipeVariantProps } from '@/styled-system/types'
import { menuItemRecipe } from './menuItemRecipe'
/**
* render a Button primitive that shows a popover showing a list of pressable items
@@ -15,15 +14,11 @@ export const MenuList = <T extends string | number = string>({
onAction: (key: T) => void
selectedItem?: T
items: Array<string | { value: T; label: ReactNode }>
} & MenuProps<unknown> &
RecipeVariantProps<typeof menuRecipe>) => {
const [variantProps] = menuRecipe.splitVariantProps(menuProps)
const classes = menuRecipe({ extraPadding: true, ...variantProps })
} & MenuProps<unknown>) => {
return (
<Menu
selectionMode={selectedItem !== undefined ? 'single' : undefined}
selectedKeys={selectedItem !== undefined ? [selectedItem] : undefined}
className={classes.root}
{...menuProps}
>
{items.map((item) => {
@@ -31,7 +26,7 @@ export const MenuList = <T extends string | number = string>({
const label = typeof item === 'string' ? item : item.label
return (
<MenuItem
className={classes.item}
className={menuItemRecipe({ extraPadding: true })}
key={value}
id={value as string}
onAction={() => {
+2 -4
View File
@@ -11,7 +11,7 @@ import {
} from 'react-aria-components'
import { Box } from './Box'
import { StyledPopover } from './Popover'
import { menuRecipe } from '@/primitives/menuRecipe.ts'
import { menuItemRecipe } from './menuItemRecipe'
const StyledButton = styled(Button, {
base: {
@@ -75,9 +75,7 @@ export const Select = <T extends string | number>({
<ListBox>
{items.map((item) => (
<ListBoxItem
className={
menuRecipe({ extraPadding: true, variant: 'light' }).item
}
className={menuItemRecipe({ extraPadding: true })}
id={item.value}
key={item.value}
>
+5 -1
View File
@@ -66,8 +66,12 @@ const StyledTab = styled(RACTab, {
color: 'box.text',
},
'&[data-selected]': {
backgroundColor: 'primary.800',
backgroundColor: 'primary',
color: 'white',
'&[data-hovered]': {
backgroundColor: 'primary',
color: 'white',
},
},
},
},
@@ -43,7 +43,7 @@ const StyledTooltip = styled(RACTooltip, {
base: {
boxShadow: '0 8px 20px rgba(0 0 0 / 0.1)',
borderRadius: '4px',
backgroundColor: 'primaryDark.100',
backgroundColor: 'gray.800',
color: 'gray.100',
forcedColorAdjust: 'none',
outline: 'none',
+76 -149
View File
@@ -12,11 +12,25 @@ export const buttonRecipe = cva({
transition: 'background 200ms, outline 200ms, border-color 200ms',
cursor: 'pointer',
border: '1px solid transparent',
color: 'colorPalette.text',
backgroundColor: 'colorPalette',
'&[data-hovered]': {
backgroundColor: 'colorPalette.hover',
},
'&[data-pressed]': {
backgroundColor: 'colorPalette.active',
},
'&[data-selected]': {
backgroundColor: 'colorPalette.active',
},
'&[data-disabled]': {
cursor: 'auto',
},
},
variants: {
size: {
default: {
borderRadius: 4,
borderRadius: 8,
paddingX: '1',
paddingY: '0.625',
'--square-padding': '{spacing.0.625}',
@@ -39,147 +53,20 @@ export const buttonRecipe = cva({
},
},
variant: {
default: {
colorPalette: 'control',
borderColor: 'control.subtle',
},
primary: {
backgroundColor: 'primary.800',
color: 'white',
'&[data-hovered]': {
backgroundColor: 'primary.action',
},
'&[data-pressed]': {
backgroundColor: 'primary.action',
},
colorPalette: 'primary',
'&[data-disabled]': {
backgroundColor: 'greyscale.100',
color: 'greyscale.400',
},
},
secondary: {
backgroundColor: 'white',
color: 'primary.800',
borderColor: 'primary.800',
'&[data-hovered]': {
backgroundColor: 'greyscale.100',
},
'&[data-pressed]': {
backgroundColor: 'greyscale.100',
},
},
secondaryText: {
backgroundColor: 'transparent',
color: 'primary.800',
'&[data-hovered]': {
backgroundColor: 'greyscale.100',
},
'&[data-pressed]': {
backgroundColor: 'greyscale.100',
},
'&[data-disabled]': {
color: 'greyscale.400',
},
},
tertiary: {
backgroundColor: 'primary.100',
color: 'primary.800',
'&[data-hovered]': {
backgroundColor: 'primary.300',
},
'&[data-pressed]': {
backgroundColor: 'primary.300',
},
},
tertiaryText: {
backgroundColor: 'transparent',
color: 'primary.900',
'&[data-hovered]': {
backgroundColor: 'primary.300',
},
'&[data-pressed]': {
backgroundColor: 'primary.300',
},
},
primaryDark: {
backgroundColor: 'primaryDark.100',
color: 'white',
'&[data-pressed]': {
backgroundColor: 'primaryDark.900',
color: 'primaryDark.100',
},
'&[data-hovered]': {
backgroundColor: 'primaryDark.300',
color: 'white',
},
'&[data-selected]': {
backgroundColor: 'primaryDark.700 !important',
color: 'primaryDark.100 !important',
},
},
primaryTextDark: {
backgroundColor: 'transparent',
color: 'white',
'&[data-hovered]': {
backgroundColor: 'primaryDark.100',
},
'&[data-pressed]': {
backgroundColor: 'primaryDark.700',
color: 'primaryDark.100',
},
'&[data-selected]': {
backgroundColor: 'primaryDark.700',
color: 'primaryDark.100',
},
},
greyscale: {
backgroundColor: 'transparent',
color: 'greyscale.400',
'&[data-hovered]': {
color: 'greyscale.800',
},
'&[data-pressed]': {
color: 'greyscale.800',
},
'&[data-selected]': {
color: 'greyscale.800',
},
'&[data-disabled]': {
color: 'greyscale.200',
},
},
danger: {
backgroundColor: 'error.400',
color: 'white',
'&[data-hovered]': {
backgroundColor: 'error.600',
},
'&[data-pressed]': {
backgroundColor: 'error.700',
color: 'error.200',
},
},
error2: {
backgroundColor: 'error.200',
color: 'error.900',
'&[data-hovered]': {
backgroundColor: 'error.300',
},
'&[data-focused]': {
backgroundColor: 'error.200',
},
'&[data-pressed]': {
backgroundColor: 'error.900',
color: 'error.100',
},
'&[data-selected]': {
backgroundColor: 'error.900 !important',
color: 'error.100 !important',
},
'&[data-disabled]': {
backgroundColor: 'error.200',
color: 'error.300',
opacity: 0.3,
},
},
// @TODO: better handling of colors… this is a mess
success: {
colorPalette: 'success',
borderColor: 'success.300',
color: 'success.subtle-text',
backgroundColor: 'success.subtle',
'&[data-hovered]': {
@@ -196,6 +83,31 @@ export const buttonRecipe = cva({
color: 'primary !important',
},
},
danger: {
colorPalette: 'danger',
borderColor: 'danger.600',
color: 'danger.subtle-text',
backgroundColor: 'danger.subtle',
'&[data-hovered]': {
backgroundColor: 'danger.200',
},
'&[data-pressed]': {
backgroundColor: 'danger.subtle!',
},
},
},
outline: {
true: {
color: 'colorPalette',
backgroundColor: 'transparent!',
borderColor: 'currentcolor!',
'&[data-hovered]': {
backgroundColor: 'colorPalette.subtle!',
},
'&[data-pressed]': {
backgroundColor: 'colorPalette.subtle!',
},
},
},
invisible: {
true: {
@@ -218,10 +130,6 @@ export const buttonRecipe = cva({
width: 'full',
},
},
// some toggle buttons make more sense without a "pushed button" style when selected because their content changes to mark the state
shySelected: {
true: {},
},
// if the button is next to other ones to make a "button group", tell where the button is to handle radius
groupPosition: {
left: {
@@ -237,21 +145,40 @@ export const buttonRecipe = cva({
borderRadius: 0,
},
},
},
compoundVariants: [
{
variant: 'primaryDark',
shySelected: true,
css: {
// some toggle buttons make more sense without a "pushed button" style when selected because their content changes to mark the state
toggledStyles: {
false: {
'&[data-selected]': {
backgroundColor: 'primaryDark.100',
color: 'white',
backgroundColor: 'colorPalette',
},
},
},
],
legacyStyle: {
true: {
borderColor: 'gray.400',
transition: 'border 200ms, background 200ms, color 200ms',
'&[data-hovered]': {
borderColor: 'gray.500',
},
'&[data-pressed]': {
borderColor: 'gray.500',
},
'&[data-selected]': {
backgroundColor: '#1d4ed8',
color: 'white',
borderColor: 'gray.500',
'&[data-hovered]': {
borderColor: '#6b7280',
backgroundColor: '#1e40af',
},
},
},
},
},
defaultVariants: {
size: 'default',
variant: 'primary',
variant: 'default',
outline: false,
toggledStyles: true,
},
})
@@ -0,0 +1,54 @@
import { cva } from '@/styled-system/css'
/**
* reusable styles for a menu item, select item, etc… to be used with panda `css()` or `styled()`
*
* these are in their own files because react hot refresh doesn't like exporting stuff
* that aren't components in component files
*/
export const menuItemRecipe = cva({
base: {
paddingY: 0.125,
paddingX: 0.5,
textAlign: 'left',
width: 'full',
borderRadius: 4,
cursor: 'pointer',
color: 'box.text',
border: '1px solid transparent',
position: 'relative',
'&[data-selected]': {
'&::before': {
content: '"✓"',
position: 'absolute',
top: '2px',
left: '6px',
},
},
'&[data-focused]': {
color: 'primary.text',
backgroundColor: 'primary',
outline: 'none!',
},
'&[data-hovered]': {
color: 'primary.text',
backgroundColor: 'primary',
outline: 'none!',
},
},
variants: {
icon: {
true: {
display: 'flex',
alignItems: 'center',
gap: '1rem',
paddingY: '0.4rem',
},
},
extraPadding: {
true: {
paddingLeft: 1.5,
},
},
},
})
-72
View File
@@ -1,72 +0,0 @@
import { sva } from '@/styled-system/css'
export const menuRecipe = sva({
slots: ['root', 'item'],
base: {
root: {},
item: {
paddingY: 0.125,
paddingX: 0.5,
textAlign: 'left',
width: 'full',
borderRadius: 4,
cursor: 'pointer',
color: 'box.text',
border: '1px solid transparent',
position: 'relative',
'&[data-selected]': {
'&::before': {
content: '"✓"',
position: 'absolute',
top: '2px',
left: '6px',
},
},
'&[data-focused]': {
color: 'primary.text',
backgroundColor: 'primaryDark.100',
outline: 'none!',
},
'&[data-hovered]': {
color: 'primary.text',
backgroundColor: 'primaryDark.100',
outline: 'none!',
},
},
},
variants: {
variant: {
light: {
item: {
'&[data-focused]': {
backgroundColor: 'primary.800',
},
'&[data-hovered]': {
backgroundColor: 'primary.800',
},
},
},
dark: {},
},
extraPadding: {
true: {
item: {
paddingLeft: 1.5,
},
},
},
icon: {
true: {
item: {
display: 'flex',
alignItems: 'center',
gap: '1rem',
paddingY: '0.4rem',
},
},
},
},
defaultVariants: {
variant: 'dark',
},
})
-2
View File
@@ -3,12 +3,10 @@ import { PanelId } from '@/features/rooms/livekit/hooks/useSidePanel'
type State = {
showHeader: boolean
showFooter: boolean
activePanelId: PanelId | null
}
export const layoutStore = proxy<State>({
showHeader: false,
showFooter: false,
activePanelId: null,
})
-4
View File
@@ -25,7 +25,3 @@ body,
transition: none !important;
}
}
body:has(.lk-video-conference) #crisp-chatbox > div > a {
display: none !important;
}
File diff suppressed because it is too large Load Diff
@@ -54,13 +54,6 @@ backend:
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_REGION_NAME: local
RECORDING_ENABLE: True
RECORDING_VERIFY_SSL: False
RECORDING_STORAGE_EVENT_ENABLE: True
RECORDING_STORAGE_EVENT_TOKEN: password
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/
SUMMARY_SERVICE_API_TOKEN: password
migrate:
@@ -115,61 +108,3 @@ posthog:
ingressAssets:
enabled: false
summary:
replicas: 1
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: minio.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
OPENAI_API_KEY: password
AWS_S3_SECURE_ACCESS: False
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
command:
- "uvicorn"
- "summary.main:app"
- "--host"
- "0.0.0.0"
- "--port"
- "8000"
- "--reload"
celery:
replicas: 1
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN: password
AWS_STORAGE_BUCKET_NAME: meet-media-storage
AWS_S3_ENDPOINT_URL: minio.meet.svc.cluster.local:9000
AWS_S3_ACCESS_KEY_ID: meet
AWS_S3_SECRET_ACCESS_KEY: password
OPENAI_API_KEY: password
AWS_S3_SECURE_ACCESS: False
WEBHOOK_API_TOKEN: password
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL: redis://default:pass@redis-master:6379/1
CELERY_RESULT_BACKEND: redis://default:pass@redis-master:6379/1
image:
repository: localhost:5001/meet-summary
pullPolicy: Always
tag: "latest"
command:
- "celery"
- "-A"
- "summary.core.celery_worker"
- "worker"
- "--pool=solo"
- "--loglevel=info"
@@ -1,7 +1,7 @@
image:
repository: lasuite/meet-backend
pullPolicy: Always
tag: "v0.1.9"
tag: "v0.1.8"
backend:
migrateJobAnnotations:
@@ -28,7 +28,6 @@ backend:
DJANGO_EMAIL_HOST: "snap-mail.numerique.gouv.fr"
DJANGO_EMAIL_PORT: 465
DJANGO_EMAIL_USE_SSL: True
DJANGO_SENTRY_DSN: https://5aead03f03505da5130af6d642c42faf@sentry.incubateur.net/202
OIDC_OP_JWKS_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/jwks
OIDC_OP_AUTHORIZATION_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/authorize
OIDC_OP_TOKEN_ENDPOINT: https://auth.agentconnect.gouv.fr/api/v2/token
@@ -129,7 +128,7 @@ frontend:
image:
repository: lasuite/meet-frontend
pullPolicy: Always
tag: "v0.1.9"
tag: "v0.1.8"
ingress:
enabled: true
@@ -27,7 +27,6 @@ backend:
DJANGO_EMAIL_HOST: "snap-mail.numerique.gouv.fr"
DJANGO_EMAIL_PORT: 465
DJANGO_EMAIL_USE_SSL: True
DJANGO_SENTRY_DSN: https://5aead03f03505da5130af6d642c42faf@sentry.incubateur.net/202
OIDC_OP_JWKS_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/jwks
OIDC_OP_AUTHORIZATION_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/authorize
OIDC_OP_TOKEN_ENDPOINT: https://fca.integ01.dev-agentconnect.fr/api/v2/token
@@ -114,17 +113,6 @@ backend:
name: meet-media-storage.bucket.libre.sh
key: bucket
AWS_S3_REGION_NAME: local
RECORDING_ENABLE: True
RECORDING_STORAGE_EVENT_ENABLE: True
RECORDING_STORAGE_EVENT_TOKEN:
secretKeyRef:
name: backend
key: RECORDING_STORAGE_EVENT_TOKEN
SUMMARY_SERVICE_ENDPOINT: http://meet-summary:80/api/v1/tasks/
SUMMARY_SERVICE_API_TOKEN:
secretKeyRef:
name: summary
key: APP_API_TOKEN
createsuperuser:
command:
@@ -186,109 +174,3 @@ posthog:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/upstream-vhost: eu-assets.i.posthog.com
nginx.ingress.kubernetes.io/backend-protocol: https
summary:
replicas: 1
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN:
secretKeyRef:
name: summary
key: APP_API_TOKEN
AWS_S3_ENDPOINT_URL:
secretKeyRef:
name: meet-media-storage.bucket.libre.sh
key: url
AWS_S3_ACCESS_KEY_ID:
secretKeyRef:
name: meet-media-storage.bucket.libre.sh
key: accessKey
AWS_S3_SECRET_ACCESS_KEY:
secretKeyRef:
name: meet-media-storage.bucket.libre.sh
key: secretKey
AWS_STORAGE_BUCKET_NAME:
secretKeyRef:
name: meet-media-storage.bucket.libre.sh
key: bucket
AWS_S3_REGION_NAME: local
OPENAI_API_KEY:
secretKeyRef:
name: summary
key: OPENAI_API_KEY
WEBHOOK_API_TOKEN:
secretKeyRef:
name: summary
key: WEBHOOK_API_TOKEN
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL:
secretKeyRef:
name: redis-summary.redis.libre.sh
key: url
CELERY_RESULT_BACKEND:
secretKeyRef:
name: redis-summary.redis.libre.sh
key: url
image:
repository: lasuite/meet-summary
pullPolicy: Always
tag: "main"
celery:
replicas: 1
envVars:
APP_NAME: summary-microservice
APP_API_TOKEN:
secretKeyRef:
name: summary
key: APP_API_TOKEN
AWS_S3_ENDPOINT_URL:
secretKeyRef:
name: meet-media-storage.bucket.libre.sh
key: endpoint
AWS_S3_ACCESS_KEY_ID:
secretKeyRef:
name: meet-media-storage.bucket.libre.sh
key: accessKey
AWS_S3_SECRET_ACCESS_KEY:
secretKeyRef:
name: meet-media-storage.bucket.libre.sh
key: secretKey
AWS_STORAGE_BUCKET_NAME:
secretKeyRef:
name: meet-media-storage.bucket.libre.sh
key: bucket
AWS_S3_REGION_NAME: local
OPENAI_API_KEY:
secretKeyRef:
name: summary
key: OPENAI_API_KEY
WEBHOOK_API_TOKEN:
secretKeyRef:
name: summary
key: WEBHOOK_API_TOKEN
WEBHOOK_URL: https://www.mock-impress.com/webhook/
CELERY_BROKER_URL:
secretKeyRef:
name: redis-summary.redis.libre.sh
key: url
CELERY_RESULT_BACKEND:
secretKeyRef:
name: redis-summary.redis.libre.sh
key: url
SENTRY_IS_ENABLED: True
SENTRY_DSN: https://5aead03f03505da5130af6d642c42faf@sentry.incubateur.net/202
image:
repository: lasuite/meet-summary
pullPolicy: Always
tag: "main"
command:
- "celery"
- "-A"
- "summary.core.celery_worker"
- "worker"
- "--pool=solo"
- "--loglevel=info"
@@ -1,7 +0,0 @@
apiVersion: core.libre.sh/v1alpha1
kind: Redis
metadata:
name: redis-summary
namespace: {{ .Release.Namespace | quote }}
spec:
disableAuth: false
-7
View File
@@ -66,13 +66,6 @@ releases:
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
kubernetes.io/ingress.class: nginx
extraVolumes:
- name: mkcert
secret:
secretName: mkcert
extraVolumeMounts:
- mountPath: /certs/CAs/
name: mkcert
- name: redis
installed: {{ eq .Environment.Name "dev" | toYaml }}
+54 -177
View File
@@ -5,9 +5,9 @@
### General configuration
| Name | Description | Value |
| ------------------------------------------ | ---------------------------------------------------- | ---------------------- |
| `image.repository` | Repository to use to pull meet's container image | `lasuite/meet-backend` |
| `image.tag` | meet's container tag | `latest` |
| ------------------------------------------ | ---------------------------------------------------- |------------------------|
| `image.repository` | Repository to use to pull impress's container image | `lasuite/meet-backend` |
| `image.tag` | impress's container tag | `latest` |
| `image.pullPolicy` | Container image pull policy | `IfNotPresent` |
| `image.credentials.username` | Username for container registry authentication | |
| `image.credentials.password` | Password for container registry authentication | |
@@ -17,7 +17,7 @@
| `fullnameOverride` | Override the full application name | `""` |
| `ingress.enabled` | whether to enable the Ingress or not | `false` |
| `ingress.className` | IngressClass to use for the Ingress | `nil` |
| `ingress.host` | Host for the Ingress | `meet.example.com` |
| `ingress.host` | Host for the Ingress | `impress.example.com` |
| `ingress.path` | Path to use for the Ingress | `/` |
| `ingress.hosts` | Additional host to configure for the Ingress | `[]` |
| `ingress.tls.enabled` | Weather to enable TLS for the Ingress | `true` |
@@ -26,7 +26,7 @@
| `ingress.customBackends` | Add custom backends to ingress | `[]` |
| `ingressAdmin.enabled` | whether to enable the Ingress or not | `false` |
| `ingressAdmin.className` | IngressClass to use for the Ingress | `nil` |
| `ingressAdmin.host` | Host for the Ingress | `meet.example.com` |
| `ingressAdmin.host` | Host for the Ingress | `impress.example.com` |
| `ingressAdmin.path` | Path to use for the Ingress | `/admin` |
| `ingressAdmin.hosts` | Additional host to configure for the Ingress | `[]` |
| `ingressAdmin.tls.enabled` | Weather to enable TLS for the Ingress | `true` |
@@ -42,7 +42,6 @@
| `backend.replicas` | Amount of backend replicas | `3` |
| `backend.shareProcessNamespace` | Enable share process namespace between containers | `false` |
| `backend.sidecars` | Add sidecars containers to backend deployment | `[]` |
| `backend.migrateJobAnnotations` | Annotations for the migrate job | `{}` |
| `backend.securityContext` | Configure backend Pod security context | `nil` |
| `backend.envVars` | Configure backend container environment variables | `undefined` |
| `backend.envVars.BY_VALUE` | Example environment variable by setting value directly | |
@@ -59,16 +58,16 @@
| `backend.migrate.restartPolicy` | backend migrate job restart policy | `Never` |
| `backend.probes.liveness.path` | Configure path for backend HTTP liveness probe | `/__heartbeat__` |
| `backend.probes.liveness.targetPort` | Configure port for backend HTTP liveness probe | `undefined` |
| `backend.probes.liveness.initialDelaySeconds` | Configure initial delay for backend liveness probe | `30` |
| `backend.probes.liveness.initialDelaySeconds` | Configure timeout for backend liveness probe | `30` |
| `backend.probes.liveness.initialDelaySeconds` | Configure initial delay for backend liveness probe | `10` |
| `backend.probes.liveness.initialDelaySeconds` | Configure timeout for backend liveness probe | `10` |
| `backend.probes.startup.path` | Configure path for backend HTTP startup probe | `undefined` |
| `backend.probes.startup.targetPort` | Configure port for backend HTTP startup probe | `undefined` |
| `backend.probes.startup.initialDelaySeconds` | Configure initial delay for backend startup probe | `undefined` |
| `backend.probes.startup.initialDelaySeconds` | Configure timeout for backend startup probe | `undefined` |
| `backend.probes.readiness.path` | Configure path for backend HTTP readiness probe | `/__lbheartbeat__` |
| `backend.probes.readiness.targetPort` | Configure port for backend HTTP readiness probe | `undefined` |
| `backend.probes.readiness.initialDelaySeconds` | Configure initial delay for backend readiness probe | `30` |
| `backend.probes.readiness.initialDelaySeconds` | Configure timeout for backend readiness probe | `30` |
| `backend.probes.readiness.initialDelaySeconds` | Configure initial delay for backend readiness probe | `10` |
| `backend.probes.readiness.initialDelaySeconds` | Configure timeout for backend readiness probe | `10` |
| `backend.resources` | Resource requirements for the backend container | `{}` |
| `backend.nodeSelector` | Node selector for the backend Pod | `{}` |
| `backend.tolerations` | Tolerations for the backend Pod | `[]` |
@@ -82,170 +81,48 @@
### frontend
| Name | Description | Value |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------- | ----------------------- |
| `frontend.image.repository` | Repository to use to pull meet's frontend container image | `lasuite/meet-frontend` |
| `frontend.image.tag` | meet's frontend container tag | `latest` |
| `frontend.image.pullPolicy` | frontend container image pull policy | `IfNotPresent` |
| `frontend.command` | Override the frontend container command | `[]` |
| `frontend.args` | Override the frontend container args | `[]` |
| `frontend.replicas` | Amount of frontend replicas | `3` |
| `frontend.shareProcessNamespace` | Enable share process namefrontend between containers | `false` |
| `frontend.sidecars` | Add sidecars containers to frontend deployment | `[]` |
| `frontend.securityContext` | Configure frontend Pod security context | `nil` |
| `frontend.envVars` | Configure frontend container environment variables | `undefined` |
| `frontend.envVars.BY_VALUE` | Example environment variable by setting value directly | |
| `frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | |
| `frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | |
| `frontend.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | |
| `frontend.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | |
| `frontend.podAnnotations` | Annotations to add to the frontend Pod | `{}` |
| `frontend.service.type` | frontend Service type | `ClusterIP` |
| `frontend.service.port` | frontend Service listening port | `80` |
| `frontend.service.targetPort` | frontend container listening port | `8080` |
| `frontend.service.annotations` | Annotations to add to the frontend Service | `{}` |
| `frontend.probes` | Configure probe for frontend | `{}` |
| `frontend.probes.liveness.path` | Configure path for frontend HTTP liveness probe | |
| `frontend.probes.liveness.targetPort` | Configure port for frontend HTTP liveness probe | |
| `frontend.probes.liveness.initialDelaySeconds` | Configure initial delay for frontend liveness probe | |
| `frontend.probes.liveness.initialDelaySeconds` | Configure timeout for frontend liveness probe | |
| `frontend.probes.startup.path` | Configure path for frontend HTTP startup probe | |
| `frontend.probes.startup.targetPort` | Configure port for frontend HTTP startup probe | |
| `frontend.probes.startup.initialDelaySeconds` | Configure initial delay for frontend startup probe | |
| `frontend.probes.startup.initialDelaySeconds` | Configure timeout for frontend startup probe | |
| `frontend.probes.readiness.path` | Configure path for frontend HTTP readiness probe | |
| `frontend.probes.readiness.targetPort` | Configure port for frontend HTTP readiness probe | |
| `frontend.probes.readiness.initialDelaySeconds` | Configure initial delay for frontend readiness probe | |
| `frontend.probes.readiness.initialDelaySeconds` | Configure timeout for frontend readiness probe | |
| `frontend.resources` | Resource requirements for the frontend container | `{}` |
| `frontend.nodeSelector` | Node selector for the frontend Pod | `{}` |
| `frontend.tolerations` | Tolerations for the frontend Pod | `[]` |
| `frontend.affinity` | Affinity for the frontend Pod | `{}` |
| `frontend.persistence` | Additional volumes to create and mount on the frontend. Used for debugging purposes | `{}` |
| `frontend.persistence.volume-name.size` | Size of the additional volume | |
| `frontend.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | |
| `frontend.persistence.volume-name.mountPath` | Path where the volume should be mounted to | |
| `frontend.extraVolumeMounts` | Additional volumes to mount on the frontend. | `[]` |
| `frontend.extraVolumes` | Additional volumes to mount on the frontend. | `[]` |
### posthog
| Name | Description | Value |
| -------------------------------------- | ----------------------------------------------------------- | ------------------------- |
| `posthog.ingress.enabled` | Enable or disable the ingress resource creation | `false` |
| `posthog.ingress.className` | Kubernetes ingress class name to use (e.g., nginx, traefik) | `nil` |
| `posthog.ingress.host` | Primary hostname for the ingress resource | `meet.example.com` |
| `posthog.ingress.path` | URL path prefix for the ingress routes (e.g., /) | `/` |
| `posthog.ingress.hosts` | Additional hostnames array to be included in the ingress | `[]` |
| `posthog.ingress.tls.enabled` | Enable or disable TLS/HTTPS for the ingress | `true` |
| `posthog.ingress.tls.additional` | Additional TLS configurations for extra hosts/certificates | `[]` |
| `posthog.ingress.customBackends` | Custom backend service configurations for the ingress | `[]` |
| `posthog.ingress.annotations` | Additional Kubernetes annotations to apply to the ingress | `{}` |
| `posthog.ingressAssets.enabled` | Enable or disable the ingress resource creation | `false` |
| `posthog.ingressAssets.className` | Kubernetes ingress class name to use (e.g., nginx, traefik) | `nil` |
| `posthog.ingressAssets.host` | Primary hostname for the ingress resource | `meet.example.com` |
| `posthog.ingressAssets.path` | URL path prefix for the ingress routes (e.g., /) | `/static` |
| `posthog.ingressAssets.hosts` | Additional hostnames array to be included in the ingress | `[]` |
| `posthog.ingressAssets.tls.enabled` | Enable or disable TLS/HTTPS for the ingress | `true` |
| `posthog.ingressAssets.tls.additional` | Additional TLS configurations for extra hosts/certificates | `[]` |
| `posthog.ingressAssets.customBackends` | Custom backend service configurations for the ingress | `[]` |
| `posthog.ingressAssets.annotations` | Additional Kubernetes annotations to apply to the ingress | `{}` |
| `posthog.service.type` | Service type (e.g. ExternalName, ClusterIP, LoadBalancer) | `ExternalName` |
| `posthog.service.externalName` | External service hostname when type is ExternalName | `eu.i.posthog.com` |
| `posthog.service.port` | Port number for the service | `443` |
| `posthog.service.annotations` | Additional annotations to apply to the service | `{}` |
| `posthog.assetsService.type` | Service type (e.g. ExternalName, ClusterIP, LoadBalancer) | `ExternalName` |
| `posthog.assetsService.externalName` | External service hostname when type is ExternalName | `eu-assets.i.posthog.com` |
| `posthog.assetsService.port` | Port number for the service | `443` |
| `posthog.assetsService.annotations` | Additional annotations to apply to the service | `{}` |
### summary
| Name | Description | Value |
| ----------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------ |
| `summary.command` | Override the summary container command | `[]` |
| `summary.args` | Override the summary container args | `[]` |
| `summary.replicas` | Amount of summary replicas | `1` |
| `summary.shareProcessNamespace` | Enable share process namespace between containers | `false` |
| `summary.sidecars` | Add sidecars containers to summary deployment | `[]` |
| `summary.migrateJobAnnotations` | Annotations for the migrate job | `{}` |
| `summary.securityContext` | Configure summary Pod security context | `nil` |
| `summary.envVars` | Configure summary container environment variables | `undefined` |
| `summary.envVars.BY_VALUE` | Example environment variable by setting value directly | |
| `summary.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | |
| `summary.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | |
| `summary.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | |
| `summary.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | |
| `summary.podAnnotations` | Annotations to add to the summary Pod | `{}` |
| `summary.service.type` | summary Service type | `ClusterIP` |
| `summary.service.port` | summary Service listening port | `80` |
| `summary.service.targetPort` | summary container listening port | `8000` |
| `summary.service.annotations` | Annotations to add to the summary Service | `{}` |
| `summary.probes.liveness.path` | Configure path for summary HTTP liveness probe | `/__heartbeat__` |
| `summary.probes.liveness.targetPort` | Configure port for summary HTTP liveness probe | `undefined` |
| `summary.probes.liveness.initialDelaySeconds` | Configure initial delay for summary liveness probe | `30` |
| `summary.probes.liveness.initialDelaySeconds` | Configure timeout for summary liveness probe | `30` |
| `summary.probes.startup.path` | Configure path for summary HTTP startup probe | `undefined` |
| `summary.probes.startup.targetPort` | Configure port for summary HTTP startup probe | `undefined` |
| `summary.probes.startup.initialDelaySeconds` | Configure initial delay for summary startup probe | `undefined` |
| `summary.probes.startup.initialDelaySeconds` | Configure timeout for summary startup probe | `undefined` |
| `summary.probes.readiness.path` | Configure path for summary HTTP readiness probe | `/__lbheartbeat__` |
| `summary.probes.readiness.targetPort` | Configure port for summary HTTP readiness probe | `undefined` |
| `summary.probes.readiness.initialDelaySeconds` | Configure initial delay for summary readiness probe | `30` |
| `summary.probes.readiness.initialDelaySeconds` | Configure timeout for summary readiness probe | `30` |
| `summary.resources` | Resource requirements for the summary container | `{}` |
| `summary.nodeSelector` | Node selector for the summary Pod | `{}` |
| `summary.tolerations` | Tolerations for the summary Pod | `[]` |
| `summary.affinity` | Affinity for the summary Pod | `{}` |
| `summary.persistence` | Additional volumes to create and mount on the summary. Used for debugging purposes | `{}` |
| `summary.persistence.volume-name.size` | Size of the additional volume | |
| `summary.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | |
| `summary.persistence.volume-name.mountPath` | Path where the volume should be mounted to | |
| `summary.extraVolumeMounts` | Additional volumes to mount on the summary. | `[]` |
| `summary.extraVolumes` | Additional volumes to mount on the summary. | `[]` |
### celery
| Name | Description | Value |
| ---------------------------------------------------- | --------------------------------------------------------------------------------- | ----------- |
| `celery.command` | Override the celery container command | `[]` |
| `celery.args` | Override the celery container args | `[]` |
| `celery.replicas` | Amount of celery replicas | `1` |
| `celery.shareProcessNamespace` | Enable share process namespace between containers | `false` |
| `celery.sidecars` | Add sidecars containers to celery deployment | `[]` |
| `celery.migrateJobAnnotations` | Annotations for the migrate job | `{}` |
| `celery.securityContext` | Configure celery Pod security context | `nil` |
| `celery.envVars` | Configure celery container environment variables | `undefined` |
| `celery.envVars.BY_VALUE` | Example environment variable by setting value directly | |
| `celery.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | |
| `celery.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | |
| `celery.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | |
| `celery.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | |
| `celery.podAnnotations` | Annotations to add to the celery Pod | `{}` |
| `celery.service.type` | celery Service type | `ClusterIP` |
| `celery.service.port` | celery Service listening port | `80` |
| `celery.service.targetPort` | celery container listening port | `8000` |
| `celery.service.annotations` | Annotations to add to the celery Service | `{}` |
| `celery.probes` | Configure celery probes | `{}` |
| `celery.probes.liveness.path` | Configure path for celery HTTP liveness probe | `undefined` |
| `celery.probes.liveness.targetPort` | Configure port for celery HTTP liveness probe | `undefined` |
| `celery.probes.liveness.initialDelaySeconds` | Configure initial delay for celery liveness probe | `undefined` |
| `celery.probes.liveness.initialDelaySeconds` | Configure timeout for celery liveness probe | `undefined` |
| `celery.probes.startup.path` | Configure path for celery HTTP startup probe | `undefined` |
| `celery.probes.startup.targetPort` | Configure port for celery HTTP startup probe | `undefined` |
| `celery.probes.startup.initialDelaySeconds` | Configure initial delay for celery startup probe | `undefined` |
| `celery.probes.startup.initialDelaySeconds` | Configure timeout for celery startup probe | `undefined` |
| `celery.probes.readiness.path` | Configure path for celery HTTP readiness probe | `undefined` |
| `celery.probes.readiness.targetPort` | Configure port for celery HTTP readiness probe | `undefined` |
| `celery.probes.readiness.initialDelaySeconds` | Configure initial delay for celery readiness probe | `undefined` |
| `celery.probes.readiness.initialDelaySeconds` | Configure timeout for celery readiness probe | `undefined` |
| `celery.resources` | Resource requirements for the celery container | `{}` |
| `celery.nodeSelector` | Node selector for the celery Pod | `{}` |
| `celery.tolerations` | Tolerations for the celery Pod | `[]` |
| `celery.affinity` | Affinity for the celery Pod | `{}` |
| `celery.persistence` | Additional volumes to create and mount on the celery. Used for debugging purposes | `{}` |
| `celery.persistence.volume-name.size` | Size of the additional volume | |
| `celery.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | |
| `celery.persistence.volume-name.mountPath` | Path where the volume should be mounted to | |
| `celery.extraVolumeMounts` | Additional volumes to mount on the celery. | `[]` |
| `celery.extraVolumes` | Additional volumes to mount on the celery. | `[]` |
| Name | Description | Value |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------- | ------------------------- |
| `frontend.image.repository` | Repository to use to pull impress's frontend container image | `lasuite/meet-frontend` |
| `frontend.image.tag` | impress's frontend container tag | `latest` |
| `frontend.image.pullPolicy` | frontend container image pull policy | `IfNotPresent` |
| `frontend.command` | Override the frontend container command | `[]` |
| `frontend.args` | Override the frontend container args | `[]` |
| `frontend.replicas` | Amount of frontend replicas | `3` |
| `frontend.shareProcessNamespace` | Enable share process namefrontend between containers | `false` |
| `frontend.sidecars` | Add sidecars containers to frontend deployment | `[]` |
| `frontend.securityContext` | Configure frontend Pod security context | `nil` |
| `frontend.envVars` | Configure frontend container environment variables | `undefined` |
| `frontend.envVars.BY_VALUE` | Example environment variable by setting value directly | |
| `frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.name` | Name of a ConfigMap when configuring env vars from a ConfigMap | |
| `frontend.envVars.FROM_CONFIGMAP.configMapKeyRef.key` | Key within a ConfigMap when configuring env vars from a ConfigMap | |
| `frontend.envVars.FROM_SECRET.secretKeyRef.name` | Name of a Secret when configuring env vars from a Secret | |
| `frontend.envVars.FROM_SECRET.secretKeyRef.key` | Key within a Secret when configuring env vars from a Secret | |
| `frontend.podAnnotations` | Annotations to add to the frontend Pod | `{}` |
| `frontend.service.type` | frontend Service type | `ClusterIP` |
| `frontend.service.port` | frontend Service listening port | `80` |
| `frontend.service.targetPort` | frontend container listening port | `8080` |
| `frontend.service.annotations` | Annotations to add to the frontend Service | `{}` |
| `frontend.probes` | Configure probe for frontend | `{}` |
| `frontend.probes.liveness.path` | Configure path for frontend HTTP liveness probe | |
| `frontend.probes.liveness.targetPort` | Configure port for frontend HTTP liveness probe | |
| `frontend.probes.liveness.initialDelaySeconds` | Configure initial delay for frontend liveness probe | |
| `frontend.probes.liveness.initialDelaySeconds` | Configure timeout for frontend liveness probe | |
| `frontend.probes.startup.path` | Configure path for frontend HTTP startup probe | |
| `frontend.probes.startup.targetPort` | Configure port for frontend HTTP startup probe | |
| `frontend.probes.startup.initialDelaySeconds` | Configure initial delay for frontend startup probe | |
| `frontend.probes.startup.initialDelaySeconds` | Configure timeout for frontend startup probe | |
| `frontend.probes.readiness.path` | Configure path for frontend HTTP readiness probe | |
| `frontend.probes.readiness.targetPort` | Configure port for frontend HTTP readiness probe | |
| `frontend.probes.readiness.initialDelaySeconds` | Configure initial delay for frontend readiness probe | |
| `frontend.probes.readiness.initialDelaySeconds` | Configure timeout for frontend readiness probe | |
| `frontend.resources` | Resource requirements for the frontend container | `{}` |
| `frontend.nodeSelector` | Node selector for the frontend Pod | `{}` |
| `frontend.tolerations` | Tolerations for the frontend Pod | `[]` |
| `frontend.affinity` | Affinity for the frontend Pod | `{}` |
| `frontend.persistence` | Additional volumes to create and mount on the frontend. Used for debugging purposes | `{}` |
| `frontend.persistence.volume-name.size` | Size of the additional volume | |
| `frontend.persistence.volume-name.type` | Type of the additional volume, persistentVolumeClaim or emptyDir | |
| `frontend.persistence.volume-name.mountPath` | Path where the volume should be mounted to | |
| `frontend.extraVolumeMounts` | Additional volumes to mount on the frontend. | `[]` |
| `frontend.extraVolumes` | Additional volumes to mount on the frontend. | `[]` |

Some files were not shown because too many files have changed in this diff Show More