update superpage
This commit is contained in:
@@ -1,57 +0,0 @@
|
|||||||
from layercake.dateutils import now
|
|
||||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, TransactItems
|
|
||||||
|
|
||||||
from models import Course, Org
|
|
||||||
|
|
||||||
|
|
||||||
def create_course(
|
|
||||||
course: Course,
|
|
||||||
org: Org,
|
|
||||||
/,
|
|
||||||
persistence_layer: DynamoDBPersistenceLayer,
|
|
||||||
):
|
|
||||||
now_ = now()
|
|
||||||
transact = TransactItems(persistence_layer.table_name)
|
|
||||||
transact.put(
|
|
||||||
item={
|
|
||||||
'sk': '0',
|
|
||||||
'tenant__org_id': {org.id},
|
|
||||||
'create_date': now_,
|
|
||||||
**course.model_dump(),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
transact.put(
|
|
||||||
item={
|
|
||||||
'id': course.id,
|
|
||||||
'sk': 'tenant',
|
|
||||||
'org_id': org.id,
|
|
||||||
'name': org.name,
|
|
||||||
'create_date': now_,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return persistence_layer.transact_write_items(transact)
|
|
||||||
|
|
||||||
|
|
||||||
def update_course(
|
|
||||||
id: str,
|
|
||||||
course: Course,
|
|
||||||
/,
|
|
||||||
persistence_layer: DynamoDBPersistenceLayer,
|
|
||||||
):
|
|
||||||
now_ = now()
|
|
||||||
transact = TransactItems(persistence_layer.table_name)
|
|
||||||
transact.update(
|
|
||||||
key=KeyPair(id, '0'),
|
|
||||||
update_expr='SET #name = :name, access_period = :access_period, cert = :cert, update_date = :update_date',
|
|
||||||
expr_attr_names={
|
|
||||||
'#name': 'name',
|
|
||||||
},
|
|
||||||
expr_attr_values={
|
|
||||||
':name': course.name,
|
|
||||||
':cert': course.cert.model_dump() if course.cert else None,
|
|
||||||
':access_period': course.access_period,
|
|
||||||
':update_date': now_,
|
|
||||||
},
|
|
||||||
cond_expr='attribute_exists(sk)',
|
|
||||||
)
|
|
||||||
return persistence_layer.transact_write_items(transact)
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
from typing import TypedDict
|
|
||||||
from uuid import uuid4
|
|
||||||
|
|
||||||
from layercake.dateutils import now
|
|
||||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, TransactItems
|
|
||||||
|
|
||||||
from settings import ORDER_TABLE
|
|
||||||
|
|
||||||
|
|
||||||
class Author(TypedDict):
|
|
||||||
id: str
|
|
||||||
name: str
|
|
||||||
|
|
||||||
|
|
||||||
class Course(TypedDict):
|
|
||||||
id: str
|
|
||||||
name: str
|
|
||||||
time_in_days: int
|
|
||||||
|
|
||||||
|
|
||||||
def set_status_as_canceled(
|
|
||||||
id: str,
|
|
||||||
*,
|
|
||||||
lock_hash: str,
|
|
||||||
author: Author,
|
|
||||||
course: Course | None = None,
|
|
||||||
vacancy_key: KeyPair | None = None,
|
|
||||||
persistence_layer: DynamoDBPersistenceLayer,
|
|
||||||
):
|
|
||||||
"""Cancel the enrollment if there's a `cancel_policy`
|
|
||||||
and put its vacancy back if `vacancy_key` is provided."""
|
|
||||||
now_ = now()
|
|
||||||
transact = TransactItems(persistence_layer.table_name)
|
|
||||||
transact.update(
|
|
||||||
key=KeyPair(id, '0'),
|
|
||||||
update_expr='SET #status = :canceled, update_date = :update',
|
|
||||||
expr_attr_names={
|
|
||||||
'#status': 'status',
|
|
||||||
},
|
|
||||||
expr_attr_values={
|
|
||||||
':canceled': 'CANCELED',
|
|
||||||
':update': now_,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
transact.put(
|
|
||||||
item={
|
|
||||||
'id': id,
|
|
||||||
'sk': 'canceled_date',
|
|
||||||
'author': author,
|
|
||||||
'create_date': now_,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
transact.delete(
|
|
||||||
key=KeyPair(id, 'cancel_policy'),
|
|
||||||
cond_expr='attribute_exists(sk)',
|
|
||||||
)
|
|
||||||
# Remove schedules lifecycle events, referencies and locks
|
|
||||||
transact.delete(key=KeyPair(id, 'schedules#archive_it'))
|
|
||||||
transact.delete(key=KeyPair(id, 'schedules#no_activity'))
|
|
||||||
transact.delete(key=KeyPair(id, 'schedules#access_period_ends'))
|
|
||||||
transact.delete(key=KeyPair(id, 'schedules#does_not_access'))
|
|
||||||
transact.delete(key=KeyPair(id, 'parent_vacancy'))
|
|
||||||
transact.delete(key=KeyPair(id, 'lock'))
|
|
||||||
transact.delete(key=KeyPair('lock', lock_hash))
|
|
||||||
|
|
||||||
if vacancy_key and course:
|
|
||||||
vacancy_pk, vacancy_sk = vacancy_key.values()
|
|
||||||
org_id = vacancy_pk.removeprefix('vacancies#')
|
|
||||||
order_id, enrollment_id = vacancy_sk.split('#')
|
|
||||||
|
|
||||||
transact.condition(
|
|
||||||
key=KeyPair(order_id, '0'),
|
|
||||||
cond_expr='attribute_exists(id)',
|
|
||||||
table_name=ORDER_TABLE,
|
|
||||||
)
|
|
||||||
# Put the vacancy back and assign a new ID
|
|
||||||
transact.put(
|
|
||||||
item={
|
|
||||||
'id': f'vacancies#{org_id}',
|
|
||||||
'sk': f'{order_id}#{uuid4()}',
|
|
||||||
'course': course,
|
|
||||||
'create_date': now_,
|
|
||||||
},
|
|
||||||
cond_expr='attribute_not_exists(sk)',
|
|
||||||
)
|
|
||||||
# Set the status of `generated_items` to `ROLLBACK` to know
|
|
||||||
# which vacancy is available for reuse
|
|
||||||
transact.update(
|
|
||||||
key=KeyPair(order_id, f'generated_items#{enrollment_id}'),
|
|
||||||
update_expr='SET #status = :status, update_date = :update',
|
|
||||||
expr_attr_names={
|
|
||||||
'#status': 'status',
|
|
||||||
},
|
|
||||||
expr_attr_values={
|
|
||||||
':status': 'ROLLBACK',
|
|
||||||
':update': now_,
|
|
||||||
},
|
|
||||||
cond_expr='attribute_exists(sk)',
|
|
||||||
table_name=ORDER_TABLE,
|
|
||||||
)
|
|
||||||
|
|
||||||
return persistence_layer.transact_write_items(transact)
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
from layercake.dateutils import now
|
|
||||||
from layercake.dynamodb import DynamoDBPersistenceLayer, KeyPair, TransactItems
|
|
||||||
|
|
||||||
|
|
||||||
def update_policies(
|
|
||||||
id: str,
|
|
||||||
/,
|
|
||||||
payment_policy: dict = {},
|
|
||||||
billing_policy: dict = {},
|
|
||||||
*,
|
|
||||||
persistence_layer: DynamoDBPersistenceLayer,
|
|
||||||
):
|
|
||||||
now_ = now()
|
|
||||||
transact = TransactItems(persistence_layer.table_name)
|
|
||||||
|
|
||||||
if payment_policy:
|
|
||||||
transact.put(
|
|
||||||
item={
|
|
||||||
'id': id,
|
|
||||||
'sk': 'payment_policy',
|
|
||||||
'create_date': now_,
|
|
||||||
}
|
|
||||||
| payment_policy
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
transact.delete(key=KeyPair(id, 'payment_policy'))
|
|
||||||
|
|
||||||
if billing_policy:
|
|
||||||
transact.put(
|
|
||||||
item={
|
|
||||||
'id': id,
|
|
||||||
'sk': 'billing_policy',
|
|
||||||
'create_date': now_,
|
|
||||||
}
|
|
||||||
| billing_policy
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
transact.delete(key=KeyPair(id, 'billing_policy'))
|
|
||||||
|
|
||||||
return persistence_layer.transact_write_items(transact)
|
|
||||||
@@ -7,9 +7,9 @@ from meilisearch import Client as Meilisearch
|
|||||||
|
|
||||||
from api_gateway import JSONResponse
|
from api_gateway import JSONResponse
|
||||||
from boto3clients import dynamodb_client
|
from boto3clients import dynamodb_client
|
||||||
from course import create_course, update_course
|
|
||||||
from middlewares import AuditLogMiddleware, Tenant, TenantMiddleware
|
from middlewares import AuditLogMiddleware, Tenant, TenantMiddleware
|
||||||
from models import Course, Org
|
from models import Course, Org
|
||||||
|
from rules.course import create_course, update_course
|
||||||
from settings import (
|
from settings import (
|
||||||
COURSE_TABLE,
|
COURSE_TABLE,
|
||||||
MEILISEARCH_API_KEY,
|
MEILISEARCH_API_KEY,
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ from pydantic import UUID4, BaseModel
|
|||||||
|
|
||||||
import elastic
|
import elastic
|
||||||
from boto3clients import dynamodb_client
|
from boto3clients import dynamodb_client
|
||||||
from enrollment import set_status_as_canceled
|
|
||||||
from middlewares.audit_log_middleware import AuditLogMiddleware
|
from middlewares.audit_log_middleware import AuditLogMiddleware
|
||||||
from middlewares.authentication_middleware import User
|
from middlewares.authentication_middleware import User
|
||||||
|
from rules.enrollment import set_status_as_canceled
|
||||||
from settings import ELASTIC_CONN, ENROLLMENT_TABLE, USER_TABLE
|
from settings import ELASTIC_CONN, ENROLLMENT_TABLE, USER_TABLE
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from pydantic.main import BaseModel
|
|||||||
from typing_extensions import Literal
|
from typing_extensions import Literal
|
||||||
|
|
||||||
from boto3clients import dynamodb_client
|
from boto3clients import dynamodb_client
|
||||||
from org import update_policies
|
from rules.org import update_policies
|
||||||
from settings import USER_TABLE
|
from settings import USER_TABLE
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ from pydantic import BaseModel, EmailStr
|
|||||||
from api_gateway import JSONResponse
|
from api_gateway import JSONResponse
|
||||||
from boto3clients import dynamodb_client
|
from boto3clients import dynamodb_client
|
||||||
from middlewares import AuditLogMiddleware
|
from middlewares import AuditLogMiddleware
|
||||||
|
from rules.user import add_email, del_email, set_email_as_primary
|
||||||
from settings import USER_TABLE
|
from settings import USER_TABLE
|
||||||
from user import add_email, del_email, set_email_as_primary
|
|
||||||
|
|
||||||
|
|
||||||
class BadRequestError(MissingError, PowertoolsBadRequestError): ...
|
class BadRequestError(MissingError, PowertoolsBadRequestError): ...
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ from pydantic import BaseModel
|
|||||||
from api_gateway import JSONResponse
|
from api_gateway import JSONResponse
|
||||||
from boto3clients import dynamodb_client
|
from boto3clients import dynamodb_client
|
||||||
from middlewares.audit_log_middleware import AuditLogMiddleware
|
from middlewares.audit_log_middleware import AuditLogMiddleware
|
||||||
|
from rules.user import del_org_member
|
||||||
from settings import USER_TABLE
|
from settings import USER_TABLE
|
||||||
from user import del_org_member
|
|
||||||
|
|
||||||
|
|
||||||
class BadRequestError(MissingError, PowertoolsBadRequestError): ...
|
class BadRequestError(MissingError, PowertoolsBadRequestError): ...
|
||||||
|
|||||||
155
http-api/user.py
155
http-api/user.py
@@ -1,155 +0,0 @@
|
|||||||
from aws_lambda_powertools.event_handler.exceptions import (
|
|
||||||
BadRequestError,
|
|
||||||
)
|
|
||||||
from botocore.exceptions import ClientError
|
|
||||||
from layercake.dateutils import now
|
|
||||||
from layercake.dynamodb import (
|
|
||||||
ComposeKey,
|
|
||||||
DynamoDBPersistenceLayer,
|
|
||||||
KeyPair,
|
|
||||||
TransactItems,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def add_email(
|
|
||||||
id: str,
|
|
||||||
email: str,
|
|
||||||
/,
|
|
||||||
*,
|
|
||||||
persistence_layer: DynamoDBPersistenceLayer,
|
|
||||||
):
|
|
||||||
now_ = now()
|
|
||||||
transact = TransactItems(persistence_layer.table_name)
|
|
||||||
transact.update(
|
|
||||||
key=KeyPair(id, '0'),
|
|
||||||
update_expr='ADD emails :email',
|
|
||||||
expr_attr_values={
|
|
||||||
':email': {email},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
transact.put(
|
|
||||||
item={
|
|
||||||
'id': id,
|
|
||||||
'sk': f'emails#{email}',
|
|
||||||
'email_primary': False,
|
|
||||||
'email_verified': False,
|
|
||||||
'create_date': now_,
|
|
||||||
},
|
|
||||||
cond_expr='attribute_not_exists(sk)',
|
|
||||||
)
|
|
||||||
transact.put(
|
|
||||||
item={
|
|
||||||
'id': 'email',
|
|
||||||
'sk': email,
|
|
||||||
'user_id': id,
|
|
||||||
'create_date': now_,
|
|
||||||
},
|
|
||||||
cond_expr='attribute_not_exists(sk)',
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
return persistence_layer.transact_write_items(transact)
|
|
||||||
except ClientError:
|
|
||||||
raise BadRequestError('Email already exists.')
|
|
||||||
|
|
||||||
|
|
||||||
def del_email(
|
|
||||||
id: str,
|
|
||||||
email: str,
|
|
||||||
/,
|
|
||||||
*,
|
|
||||||
persistence_layer: DynamoDBPersistenceLayer,
|
|
||||||
) -> bool:
|
|
||||||
"""Delete any email except the primary email."""
|
|
||||||
transact = TransactItems(persistence_layer.table_name)
|
|
||||||
transact.delete(
|
|
||||||
key=KeyPair('email', email),
|
|
||||||
)
|
|
||||||
transact.delete(
|
|
||||||
key=KeyPair(id, ComposeKey(email, prefix='emails')),
|
|
||||||
cond_expr='email_primary <> :primary',
|
|
||||||
expr_attr_values={':primary': True},
|
|
||||||
)
|
|
||||||
transact.update(
|
|
||||||
key=KeyPair(id, '0'),
|
|
||||||
update_expr='DELETE emails :email',
|
|
||||||
expr_attr_values={
|
|
||||||
':email': {email},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
return persistence_layer.transact_write_items(transact)
|
|
||||||
except ClientError:
|
|
||||||
raise BadRequestError('Cannot remove the primary email.')
|
|
||||||
|
|
||||||
|
|
||||||
def set_email_as_primary(
|
|
||||||
id: str,
|
|
||||||
new_email: str,
|
|
||||||
old_email: str,
|
|
||||||
/,
|
|
||||||
*,
|
|
||||||
email_verified: bool = False,
|
|
||||||
persistence_layer: DynamoDBPersistenceLayer,
|
|
||||||
):
|
|
||||||
now_ = now()
|
|
||||||
expr = 'SET email_primary = :email_primary, update_date = :update_date'
|
|
||||||
transact = TransactItems(persistence_layer.table_name)
|
|
||||||
# Set the old email as non-primary
|
|
||||||
transact.update(
|
|
||||||
key=KeyPair(id, ComposeKey(old_email, 'emails')),
|
|
||||||
update_expr=expr,
|
|
||||||
expr_attr_values={
|
|
||||||
':email_primary': False,
|
|
||||||
':update_date': now_,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
# Set the new email as primary
|
|
||||||
transact.update(
|
|
||||||
key=KeyPair(id, ComposeKey(new_email, 'emails')),
|
|
||||||
update_expr=expr,
|
|
||||||
expr_attr_values={
|
|
||||||
':email_primary': True,
|
|
||||||
':update_date': now_,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
transact.update(
|
|
||||||
key=KeyPair(id, '0'),
|
|
||||||
update_expr=(
|
|
||||||
'SET email = :email, email_verified = :email_verified, '
|
|
||||||
'update_date = :update_date'
|
|
||||||
),
|
|
||||||
expr_attr_values={
|
|
||||||
':email': new_email,
|
|
||||||
':email_verified': email_verified,
|
|
||||||
':update_date': now_,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return persistence_layer.transact_write_items(transact)
|
|
||||||
|
|
||||||
|
|
||||||
def del_org_member(
|
|
||||||
id: str,
|
|
||||||
*,
|
|
||||||
org_id: str,
|
|
||||||
persistence_layer: DynamoDBPersistenceLayer,
|
|
||||||
) -> bool:
|
|
||||||
transact = TransactItems(persistence_layer.table_name)
|
|
||||||
|
|
||||||
# Remove the user's relationship with the organization and their privileges
|
|
||||||
transact.delete(key=KeyPair(id, f'acls#{org_id}'))
|
|
||||||
transact.delete(key=KeyPair(id, f'orgs#{org_id}'))
|
|
||||||
transact.update(
|
|
||||||
key=KeyPair(id, '0'),
|
|
||||||
update_expr='DELETE #tenant :org_id',
|
|
||||||
expr_attr_names={'#tenant': 'tenant__org_id'},
|
|
||||||
expr_attr_values={':org_id': {org_id}},
|
|
||||||
)
|
|
||||||
|
|
||||||
# Remove the user from the organization's admins and members list
|
|
||||||
transact.delete(key=KeyPair(org_id, f'admins#{id}'))
|
|
||||||
transact.delete(key=KeyPair(f'orgmembers#{org_id}', id))
|
|
||||||
|
|
||||||
return persistence_layer.transact_write_items(transact)
|
|
||||||
6
superpage/.prettierrc
Normal file
6
superpage/.prettierrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
||||||
@@ -1,14 +1,18 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config'
|
||||||
|
|
||||||
import react from '@astrojs/react';
|
import react from '@astrojs/react'
|
||||||
import tailwindcss from '@tailwindcss/vite';
|
import tailwindcss from '@tailwindcss/vite'
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [react()],
|
integrations: [react()],
|
||||||
|
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [tailwindcss()]
|
plugins: [tailwindcss()],
|
||||||
}
|
},
|
||||||
});
|
server: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
allowedHosts: ['7aaa-187-57-7-239.ngrok-free.app'],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|||||||
94
superpage/package-lock.json
generated
94
superpage/package-lock.json
generated
@@ -9,7 +9,7 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/react": "^4.2.1",
|
"@astrojs/react": "^4.2.1",
|
||||||
"@headlessui/react": "^2.2.0",
|
"@headlessui/react": "^2.2.1",
|
||||||
"@heroicons/react": "^2.2.0",
|
"@heroicons/react": "^2.2.0",
|
||||||
"@tailwindcss/vite": "^4.0.13",
|
"@tailwindcss/vite": "^4.0.13",
|
||||||
"@tanstack/react-query": "^5.68.0",
|
"@tanstack/react-query": "^5.68.0",
|
||||||
@@ -873,15 +873,15 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@headlessui/react": {
|
"node_modules/@headlessui/react": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.1.tgz",
|
||||||
"integrity": "sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ==",
|
"integrity": "sha512-daiUqVLae8CKVjEVT19P/izW0aGK0GNhMSAeMlrDebKmoVZHcRRwbxzgtnEadUVDXyBsWo9/UH4KHeniO+0tMg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/react": "^0.26.16",
|
"@floating-ui/react": "^0.26.16",
|
||||||
"@react-aria/focus": "^3.17.1",
|
"@react-aria/focus": "^3.17.1",
|
||||||
"@react-aria/interactions": "^3.21.3",
|
"@react-aria/interactions": "^3.21.3",
|
||||||
"@tanstack/react-virtual": "^3.8.1"
|
"@tanstack/react-virtual": "^3.11.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
@@ -1316,14 +1316,14 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@react-aria/focus": {
|
"node_modules/@react-aria/focus": {
|
||||||
"version": "3.20.1",
|
"version": "3.20.2",
|
||||||
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.1.tgz",
|
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.2.tgz",
|
||||||
"integrity": "sha512-lgYs+sQ1TtBrAXnAdRBQrBo0/7o5H6IrfDxec1j+VRpcXL0xyk0xPq+m3lZp8typzIghqDgpnKkJ5Jf4OrzPIw==",
|
"integrity": "sha512-Q3rouk/rzoF/3TuH6FzoAIKrl+kzZi9LHmr8S5EqLAOyP9TXIKG34x2j42dZsAhrw7TbF9gA8tBKwnCNH4ZV+Q==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-aria/interactions": "^3.24.1",
|
"@react-aria/interactions": "^3.25.0",
|
||||||
"@react-aria/utils": "^3.28.1",
|
"@react-aria/utils": "^3.28.2",
|
||||||
"@react-types/shared": "^3.28.0",
|
"@react-types/shared": "^3.29.0",
|
||||||
"@swc/helpers": "^0.5.0",
|
"@swc/helpers": "^0.5.0",
|
||||||
"clsx": "^2.0.0"
|
"clsx": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -1333,15 +1333,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-aria/interactions": {
|
"node_modules/@react-aria/interactions": {
|
||||||
"version": "3.24.1",
|
"version": "3.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.24.1.tgz",
|
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.0.tgz",
|
||||||
"integrity": "sha512-OWEcIC6UQfWq4Td5Ptuh4PZQ4LHLJr/JL2jGYvuNL6EgL3bWvzPrRYIF/R64YbfVxIC7FeZpPSkS07sZ93/NoA==",
|
"integrity": "sha512-GgIsDLlO8rDU/nFn6DfsbP9rfnzhm8QFjZkB9K9+r+MTSCn7bMntiWQgMM+5O6BiA8d7C7x4zuN4bZtc0RBdXQ==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-aria/ssr": "^3.9.7",
|
"@react-aria/ssr": "^3.9.8",
|
||||||
"@react-aria/utils": "^3.28.1",
|
"@react-aria/utils": "^3.28.2",
|
||||||
"@react-stately/flags": "^3.1.0",
|
"@react-stately/flags": "^3.1.1",
|
||||||
"@react-types/shared": "^3.28.0",
|
"@react-types/shared": "^3.29.0",
|
||||||
"@swc/helpers": "^0.5.0"
|
"@swc/helpers": "^0.5.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@@ -1350,9 +1350,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-aria/ssr": {
|
"node_modules/@react-aria/ssr": {
|
||||||
"version": "3.9.7",
|
"version": "3.9.8",
|
||||||
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.7.tgz",
|
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz",
|
||||||
"integrity": "sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==",
|
"integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@swc/helpers": "^0.5.0"
|
"@swc/helpers": "^0.5.0"
|
||||||
@@ -1365,15 +1365,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-aria/utils": {
|
"node_modules/@react-aria/utils": {
|
||||||
"version": "3.28.1",
|
"version": "3.28.2",
|
||||||
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.28.1.tgz",
|
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.28.2.tgz",
|
||||||
"integrity": "sha512-mnHFF4YOVu9BRFQ1SZSKfPhg3z+lBRYoW5mLcYTQihbKhz48+I1sqRkP7ahMITr8ANH3nb34YaMME4XWmK2Mgg==",
|
"integrity": "sha512-J8CcLbvnQgiBn54eeEvQQbIOfBF3A1QizxMw9P4cl9MkeR03ug7RnjTIdJY/n2p7t59kLeAB3tqiczhcj+Oi5w==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-aria/ssr": "^3.9.7",
|
"@react-aria/ssr": "^3.9.8",
|
||||||
"@react-stately/flags": "^3.1.0",
|
"@react-stately/flags": "^3.1.1",
|
||||||
"@react-stately/utils": "^3.10.5",
|
"@react-stately/utils": "^3.10.6",
|
||||||
"@react-types/shared": "^3.28.0",
|
"@react-types/shared": "^3.29.0",
|
||||||
"@swc/helpers": "^0.5.0",
|
"@swc/helpers": "^0.5.0",
|
||||||
"clsx": "^2.0.0"
|
"clsx": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -1383,18 +1383,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-stately/flags": {
|
"node_modules/@react-stately/flags": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.1.tgz",
|
||||||
"integrity": "sha512-KSHOCxTFpBtxhIRcKwsD1YDTaNxFtCYuAUb0KEihc16QwqZViq4hasgPBs2gYm7fHRbw7WYzWKf6ZSo/+YsFlg==",
|
"integrity": "sha512-XPR5gi5LfrPdhxZzdIlJDz/B5cBf63l4q6/AzNqVWFKgd0QqY5LvWJftXkklaIUpKSJkIKQb8dphuZXDtkWNqg==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@swc/helpers": "^0.5.0"
|
"@swc/helpers": "^0.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-stately/utils": {
|
"node_modules/@react-stately/utils": {
|
||||||
"version": "3.10.5",
|
"version": "3.10.6",
|
||||||
"resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.5.tgz",
|
"resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.6.tgz",
|
||||||
"integrity": "sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ==",
|
"integrity": "sha512-O76ip4InfTTzAJrg8OaZxKU4vvjMDOpfA/PGNOytiXwBbkct2ZeZwaimJ8Bt9W1bj5VsZ81/o/tW4BacbdDOMA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@swc/helpers": "^0.5.0"
|
"@swc/helpers": "^0.5.0"
|
||||||
@@ -1404,9 +1404,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@react-types/shared": {
|
"node_modules/@react-types/shared": {
|
||||||
"version": "3.28.0",
|
"version": "3.29.0",
|
||||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.29.0.tgz",
|
||||||
"integrity": "sha512-9oMEYIDc3sk0G5rysnYvdNrkSg7B04yTKl50HHSZVbokeHpnU0yRmsDaWb9B/5RprcKj8XszEk5guBO8Sa/Q+Q==",
|
"integrity": "sha512-IDQYu/AHgZimObzCFdNl1LpZvQW/xcfLt3v20sorl5qRucDVj4S9os98sVTZ4IRIBjmS+MkjqpR5E70xan7ooA==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||||
@@ -1757,9 +1757,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@swc/helpers": {
|
"node_modules/@swc/helpers": {
|
||||||
"version": "0.5.15",
|
"version": "0.5.17",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
|
||||||
"integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
|
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.8.0"
|
"tslib": "^2.8.0"
|
||||||
@@ -2016,12 +2016,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/react-virtual": {
|
"node_modules/@tanstack/react-virtual": {
|
||||||
"version": "3.13.2",
|
"version": "3.13.6",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.6.tgz",
|
||||||
"integrity": "sha512-LceSUgABBKF6HSsHK2ZqHzQ37IKV/jlaWbHm+NyTa3/WNb/JZVcThDuTainf+PixltOOcFCYXwxbLpOX9sCx+g==",
|
"integrity": "sha512-WT7nWs8ximoQ0CDx/ngoFP7HbQF9Q2wQe4nh2NB+u2486eX3nZRE40P9g6ccCVq7ZfTSH5gFOuCoVH5DLNS/aA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tanstack/virtual-core": "3.13.2"
|
"@tanstack/virtual-core": "3.13.6"
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@@ -2033,9 +2033,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tanstack/virtual-core": {
|
"node_modules/@tanstack/virtual-core": {
|
||||||
"version": "3.13.2",
|
"version": "3.13.6",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.6.tgz",
|
||||||
"integrity": "sha512-Qzz4EgzMbO5gKrmqUondCjiHcuu4B1ftHb0pjCut661lXZdGoHeze9f/M8iwsK1t5LGR6aNuNGU7mxkowaW6RQ==",
|
"integrity": "sha512-cnQUeWnhNP8tJ4WsGcYiX24Gjkc9ALstLbHcBj1t3E7EimN6n6kHH+DPV4PpDnuw00NApQp+ViojMj1GRdwYQg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/react": "^4.2.1",
|
"@astrojs/react": "^4.2.1",
|
||||||
"@headlessui/react": "^2.2.0",
|
"@headlessui/react": "^2.2.1",
|
||||||
"@heroicons/react": "^2.2.0",
|
"@heroicons/react": "^2.2.0",
|
||||||
"@tailwindcss/vite": "^4.0.13",
|
"@tailwindcss/vite": "^4.0.13",
|
||||||
"@tanstack/react-query": "^5.68.0",
|
"@tanstack/react-query": "^5.68.0",
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
import clsx from "clsx";
|
import clsx from 'clsx'
|
||||||
|
|
||||||
interface CardProps {
|
interface CardProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode
|
||||||
color?: "gradient" | "darker" | "yellow" | "zinc";
|
color?: 'gradient' | 'darker' | 'yellow' | 'zinc'
|
||||||
className?: string | undefined;
|
className?: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Card({ children, color = "gradient", className }: CardProps) {
|
export function Card({ children, color = 'gradient', className }: CardProps) {
|
||||||
const colorVariants = {
|
const colorVariants = {
|
||||||
gradient: "bg-linear-to-tr from-green-secondary to-yellow-primary",
|
gradient: 'bg-linear-to-tr from-[#8CD366] via-[#C7D174] to-[#FFCF82]',
|
||||||
darker: "bg-green-primary text-white",
|
darker: 'bg-green-primary text-white',
|
||||||
yellow: "text-green-primary bg-yellow-tertiary",
|
yellow: 'text-green-primary bg-yellow-tertiary',
|
||||||
zinc: "text-white bg-zinc-900",
|
zinc: 'text-white bg-zinc-900',
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"lg:rounded-2xl",
|
'lg:rounded-2xl',
|
||||||
"lg:drop-shadow-sm",
|
'lg:drop-shadow-sm',
|
||||||
"p-3 lg:p-12",
|
'p-3 lg:p-12',
|
||||||
colorVariants[color],
|
colorVariants[color],
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import clsx from "clsx";
|
import clsx from 'clsx'
|
||||||
|
|
||||||
interface ContainerProps {
|
interface ContainerProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode
|
||||||
className?: string | undefined;
|
className?: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Container({ children, className }: ContainerProps) {
|
export function Container({ children, className }: ContainerProps) {
|
||||||
return <div className={clsx("max-w-7xl mx-auto", className)}>{children}</div>;
|
return (
|
||||||
|
<div className={clsx('max-w-7xl mx-auto max-lg:px-3', className)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import { useForm } from "react-hook-form";
|
import { useForm } from 'react-hook-form'
|
||||||
import { useMutation } from "node_modules/@tanstack/react-query/build/legacy";
|
import { useMutation } from 'node_modules/@tanstack/react-query/build/legacy'
|
||||||
import { queryClient } from "../queryClient";
|
import { queryClient } from '../queryClient'
|
||||||
import axios from "axios";
|
import axios from 'axios'
|
||||||
import { createElement } from "react";
|
import { createElement } from 'react'
|
||||||
import clsx from "clsx";
|
import clsx from 'clsx'
|
||||||
|
|
||||||
interface IFormInput {
|
interface IFormInput {
|
||||||
name: string;
|
name: string
|
||||||
email: string;
|
email: string
|
||||||
message: string;
|
message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Form() {
|
export function Form() {
|
||||||
const { register, handleSubmit, reset, formState } = useForm<IFormInput>();
|
const { register, handleSubmit, reset, formState } = useForm<IFormInput>()
|
||||||
const { mutateAsync } = useMutation(
|
const { mutateAsync } = useMutation(
|
||||||
{
|
{
|
||||||
mutationFn: async (data: IFormInput) => {
|
mutationFn: async (data: IFormInput) => {
|
||||||
return await axios.post("https://n8n.sergio.run/webhook/eduseg", data);
|
return await axios.post('https://n8n.sergio.run/webhook/eduseg', data)
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
reset();
|
reset()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
queryClient,
|
queryClient,
|
||||||
);
|
)
|
||||||
|
|
||||||
const onSubmit = async (data: IFormInput) => {
|
const onSubmit = async (data: IFormInput) => {
|
||||||
await mutateAsync(data);
|
await mutateAsync(data)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
@@ -41,17 +41,17 @@ export function Form() {
|
|||||||
<div className="grid lg:grid-cols-2 gap-3 lg:gap-6">
|
<div className="grid lg:grid-cols-2 gap-3 lg:gap-6">
|
||||||
<label>
|
<label>
|
||||||
Nome
|
Nome
|
||||||
<Input {...register("name")} />
|
<Input {...register('name')} />
|
||||||
</label>
|
</label>
|
||||||
<label>
|
<label>
|
||||||
Email
|
Email
|
||||||
<Input {...register("email")} />
|
<Input {...register('email')} />
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label>
|
<label>
|
||||||
Mensagem
|
Mensagem
|
||||||
<Input as="textarea" className="h-26" {...register("message")} />
|
<Input as="textarea" className="h-26" {...register('message')} />
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -61,20 +61,20 @@ export function Form() {
|
|||||||
Quero um orçamento
|
Quero um orçamento
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IInput extends React.HTMLAttributes<HTMLElement> {
|
interface IInput extends React.HTMLAttributes<HTMLElement> {
|
||||||
as?: string;
|
as?: string
|
||||||
className?: string | undefined;
|
className?: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Input({ as = "input", className, ...props }: IInput) {
|
export function Input({ as = 'input', className, ...props }: IInput) {
|
||||||
return createElement(as, {
|
return createElement(as, {
|
||||||
className: clsx(
|
className: clsx(
|
||||||
"border border-transparent focus:border-green-secondary focus:ring ring-green-secondary text-white bg-black p-3 rounded-lg w-full outline-none",
|
'border border-transparent focus:border-green-secondary focus:ring ring-green-secondary text-white bg-black p-3 rounded-lg w-full outline-none',
|
||||||
className,
|
className,
|
||||||
),
|
),
|
||||||
...props,
|
...props,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
102
superpage/src/components/List.jsx
Normal file
102
superpage/src/components/List.jsx
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import {
|
||||||
|
Disclosure,
|
||||||
|
DisclosureButton,
|
||||||
|
DisclosurePanel,
|
||||||
|
} from '@headlessui/react'
|
||||||
|
import { ChevronDownIcon } from '@heroicons/react/24/solid'
|
||||||
|
|
||||||
|
export function Example() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ListItem defaultOpen={false}>
|
||||||
|
<ListButton>1. Introdução</ListButton>
|
||||||
|
<ListPanel>
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sit
|
||||||
|
amet neque id libero semper vulputate a ut ex. Pellentesque semper
|
||||||
|
ultrices mi in efficitur.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nulla sit amet quam eu neque convallis volutpat. Pellentesque eu
|
||||||
|
commodo sem. Suspendisse ac lobortis massa, ac mattis mauris.
|
||||||
|
Integer malesuada bibendum ante, sed consequat augue convallis et.
|
||||||
|
</p>
|
||||||
|
</ListPanel>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<ListButton>2. Aspectos gerais dos primeiros socorros</ListButton>
|
||||||
|
<ListPanel>
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sit
|
||||||
|
amet neque id libero semper vulputate a ut ex. Pellentesque semper
|
||||||
|
ultrices mi in efficitur.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nulla sit amet quam eu neque convallis volutpat. Pellentesque eu
|
||||||
|
commodo sem. Suspendisse ac lobortis massa, ac mattis mauris.
|
||||||
|
Integer malesuada bibendum ante, sed consequat augue convallis et.
|
||||||
|
</p>
|
||||||
|
</ListPanel>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<ListButton>3. Sinais vitais e avaliação primária</ListButton>
|
||||||
|
<ListPanel>
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sit
|
||||||
|
amet neque id libero semper vulputate a ut ex. Pellentesque semper
|
||||||
|
ultrices mi in efficitur.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nulla sit amet quam eu neque convallis volutpat. Pellentesque eu
|
||||||
|
commodo sem. Suspendisse ac lobortis massa, ac mattis mauris.
|
||||||
|
Integer malesuada bibendum ante, sed consequat augue convallis et.
|
||||||
|
</p>
|
||||||
|
</ListPanel>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem>
|
||||||
|
<ListButton>4. Parada cardiorrespiratória</ListButton>
|
||||||
|
<ListPanel>
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sit
|
||||||
|
amet neque id libero semper vulputate a ut ex. Pellentesque semper
|
||||||
|
ultrices mi in efficitur.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Nulla sit amet quam eu neque convallis volutpat. Pellentesque eu
|
||||||
|
commodo sem. Suspendisse ac lobortis massa, ac mattis mauris.
|
||||||
|
Integer malesuada bibendum ante, sed consequat augue convallis et.
|
||||||
|
</p>
|
||||||
|
</ListPanel>
|
||||||
|
</ListItem>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ListItem({ children, ...props }) {
|
||||||
|
return (
|
||||||
|
<Disclosure
|
||||||
|
as="div"
|
||||||
|
className="bg-white/10 rounded-lg w-full data-open:bg-white/15"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Disclosure>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ListButton({ children }) {
|
||||||
|
return (
|
||||||
|
<DisclosureButton className="group flex items-center justify-between w-full px-5 py-3 cursor-pointer">
|
||||||
|
<span className="text-left">{children}</span>
|
||||||
|
<ChevronDownIcon className="size-5 fill-white/60 group-data-[hover]:fill-white/50 group-data-[open]:rotate-180" />
|
||||||
|
</DisclosureButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ListPanel({ children }) {
|
||||||
|
return (
|
||||||
|
<DisclosurePanel className="text-sm/6 text-white/70 space-y-2 px-5 pb-3">
|
||||||
|
{children}
|
||||||
|
</DisclosurePanel>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -60,7 +60,7 @@ export function Regular(props) {
|
|||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Smallest(props) {
|
export function Smallest(props) {
|
||||||
@@ -95,5 +95,5 @@ export function Smallest(props) {
|
|||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,32 +4,32 @@ import {
|
|||||||
ComboboxInput,
|
ComboboxInput,
|
||||||
ComboboxOption,
|
ComboboxOption,
|
||||||
ComboboxOptions,
|
ComboboxOptions,
|
||||||
} from "@headlessui/react";
|
} from '@headlessui/react'
|
||||||
import { CheckIcon, ChevronDownIcon } from "@heroicons/react/20/solid";
|
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid'
|
||||||
import clsx from "clsx";
|
import clsx from 'clsx'
|
||||||
import { useState } from "react";
|
import { useState } from 'react'
|
||||||
|
|
||||||
const people = [
|
const people = [
|
||||||
{ id: 1, name: "8 horas" },
|
{ id: 1, name: '8 horas' },
|
||||||
{ id: 2, name: "40 horas" },
|
{ id: 2, name: '40 horas' },
|
||||||
];
|
]
|
||||||
|
|
||||||
export function Select() {
|
export function Select() {
|
||||||
const [query, setQuery] = useState("");
|
const [query, setQuery] = useState('')
|
||||||
const [selected, setSelected] = useState(people[1]);
|
const [selected, setSelected] = useState(people[1])
|
||||||
|
|
||||||
const filteredPeople =
|
const filteredPeople =
|
||||||
query === ""
|
query === ''
|
||||||
? people
|
? people
|
||||||
: people.filter((person) => {
|
: people.filter((person) => {
|
||||||
return person.name.toLowerCase().includes(query.toLowerCase());
|
return person.name.toLowerCase().includes(query.toLowerCase())
|
||||||
});
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Combobox
|
<Combobox
|
||||||
value={selected}
|
value={selected}
|
||||||
onChange={(value) => setSelected(value)}
|
onChange={(value) => setSelected(value)}
|
||||||
onClose={() => setQuery("")}
|
onClose={() => setQuery('')}
|
||||||
>
|
>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<ComboboxInput
|
<ComboboxInput
|
||||||
@@ -50,5 +50,5 @@ export function Select() {
|
|||||||
))}
|
))}
|
||||||
</ComboboxOptions>
|
</ComboboxOptions>
|
||||||
</Combobox>
|
</Combobox>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
import "../styles/app.css";
|
import '../styles/app.css'
|
||||||
import { Regular as Logo } from "@components/Logo";
|
import { Regular as Logo } from '@components/Logo'
|
||||||
import { Container } from "@components/Container";
|
import { Container } from '@components/Container'
|
||||||
import { Select } from "@components/Select";
|
|
||||||
import {
|
import {
|
||||||
ArrowLeftStartOnRectangleIcon,
|
ArrowLeftStartOnRectangleIcon,
|
||||||
AcademicCapIcon,
|
AcademicCapIcon,
|
||||||
} from "@heroicons/react/24/solid";
|
ChevronDownIcon,
|
||||||
|
} from '@heroicons/react/24/solid'
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<Container className="flex items-center p-3">
|
<Container className="flex items-center py-3">
|
||||||
<Logo className="h-8" />
|
<Logo className="h-8" />
|
||||||
|
|
||||||
<div class="ml-auto">
|
<div class="ml-auto">
|
||||||
@@ -31,49 +31,63 @@ import {
|
|||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
<section class="bg-lime-400 sticky top-0 z-10">
|
<section class="bg-lime-400 sticky top-0 z-10 py-3">
|
||||||
<Container className="flex items-center p-3">
|
<Container className="flex items-center">
|
||||||
<div class="flex gap-1.5 lg:gap-3 items-center">
|
<div
|
||||||
<div class="bg-black p-1.5 lg:p-3 rounded-lg lg:rounded-xl">
|
class="flex gap-1.5 lg:gap-3 items-center rounded hover:ring-2 ring-black"
|
||||||
<AcademicCapIcon className="w-5 fill-lime-400" />
|
>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex gap-1.5">
|
<div class="flex gap-1.5">
|
||||||
<div class="text-black truncate max-lg:max-w-36">
|
<div class="text-black truncate max-lg:max-w-36 font-semibold">
|
||||||
NR-18 PEMT Plataforma Móvel de Trabalho Aéreo
|
NR-18 PEMT Plataforma Móvel de Trabalho Aéreo
|
||||||
</div>
|
</div>
|
||||||
<!-- <Select client:load /> -->
|
<ChevronDownIcon className="w-5 fill-black" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ml-auto">
|
<div class="ml-auto">
|
||||||
<button
|
<button
|
||||||
class="bg-black p-2.5 rounded-md cursor-pointer text-sm"
|
class="bg-black font-semibold py-2.5 px-3 rounded-md cursor-pointer"
|
||||||
>
|
>
|
||||||
Comprar R$149,00
|
Contratar agora
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<div class="mb-12">
|
||||||
<slot />
|
<slot />
|
||||||
|
</div>
|
||||||
|
|
||||||
<footer class="bg-stone-900 p-3 lg:py-10">
|
<footer class="bg-stone-900 py-3 lg:py-10 hidden">
|
||||||
<Container className="space-y-5">
|
<Container className="space-y-5 lg:grid grid-cols-6">
|
||||||
<div class="space-y-1">
|
<div class="space-y-2.5">
|
||||||
<Logo className="h-10" />
|
<Logo className="h-10" />
|
||||||
<p class="text-sm text-green-tertiary leading-4">
|
<p class="text-sm/4 tracking-tighter text-balance">
|
||||||
Educação que garante<br />sua segurança.
|
Educação que garante<br />sua segurança.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-1">
|
<div class="space-y-1 lg:col-span-5">
|
||||||
<h6 class="text-xl">Cursos</h6>
|
<h6 class="text-2xl/4">Conheça outros cursos</h6>
|
||||||
<ul>
|
<ul class="mt-2.5">
|
||||||
<li>Lei Lucas</li>
|
<li>Lei Lucas</li>
|
||||||
<li>NR-18 PEMT Plataforma Móvel de Trabalho Aéreo</li>
|
<li>NR-18 PEMT Plataforma Móvel de Trabalho Aéreo</li>
|
||||||
<li>NR-35 Trabalho em Altura</li>
|
<li>NR-35 Trabalho em Altura</li>
|
||||||
<li>NR-10 Básico</li>
|
<li>NR-10 Básico</li>
|
||||||
|
<li>
|
||||||
|
NR-12 Máquinas e Equipamentos <span
|
||||||
|
class="bg-lime-300 py-0.5 px-1 text-black rounded text-sm/2"
|
||||||
|
>Reciclagem</span
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
NR-18 PEMT Plataforma Elevatória Móvel de Trabalho <span
|
||||||
|
class="bg-lime-300 py-0.5 px-1 text-black rounded text-sm/2"
|
||||||
|
>Reciclagem</span
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li>LOTO Lockout Tagout</li>
|
||||||
|
<li>NR-18 Sinaleiro e Amarrador de Cargas para Içamento</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
BIN
superpage/src/pages/homem-de-negocios.png
Normal file
BIN
superpage/src/pages/homem-de-negocios.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 300 KiB |
@@ -1,69 +1,226 @@
|
|||||||
---
|
---
|
||||||
import { Bookmark } from "@components/Bookmark";
|
import { Image } from 'astro:assets'
|
||||||
import { Card } from "@components/Card";
|
import { Card } from '@components/Card'
|
||||||
import { Container } from "@components/Container";
|
import { Container } from '@components/Container'
|
||||||
import { Form } from "@components/Form";
|
import { Form } from '@components/Form'
|
||||||
import Layout from "@layouts/Layout.astro";
|
import Layout from '@layouts/Layout.astro'
|
||||||
|
import { Example } from '@components/List'
|
||||||
|
import {
|
||||||
|
AcademicCapIcon,
|
||||||
|
GlobeAmericasIcon,
|
||||||
|
PhoneIcon,
|
||||||
|
BanknotesIcon,
|
||||||
|
StarIcon,
|
||||||
|
ClockIcon,
|
||||||
|
} from '@heroicons/react/24/outline'
|
||||||
|
import { CheckBadgeIcon } from '@heroicons/react/24/solid'
|
||||||
|
import nr18plataforma from './nr18-plataforma.png'
|
||||||
|
import mulherdenegocios from './mulher-de-negocios.png'
|
||||||
|
import homemdenegocios from './homem-de-negocios.png'
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout>
|
<Layout>
|
||||||
<Container className="lg:space-y-5 lg:p-3">
|
<Container className="py-3 lg:py-12 lg:flex items-center gap-5">
|
||||||
<!-- <div class="max-lg:px-5">
|
<Image
|
||||||
<Bookmark className="h-96" />
|
src={nr18plataforma}
|
||||||
</div> -->
|
alt="NR-18"
|
||||||
<Card>
|
class="size-1/3 object-bottom hidden lg:block"
|
||||||
<p>
|
/>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam
|
<section>
|
||||||
quis mattis tortor, sit amet mollis lorem. In imperdiet, ante
|
<div class="space-y-5">
|
||||||
sit amet maximus dictum, est elit ultrices lacus, in placerat
|
<span class="font-medium">Curso de formação</span>
|
||||||
ante risus vel massa. Maecenas porta purus non feugiat
|
<h1 class="font-semibold text-5xl lg:text-7xl">
|
||||||
venenatis. Sed tempus quam id commodo interdum. Aliquam id
|
NR-18 PEMT Plataforma Móvel de Trabalho Aéreo
|
||||||
ullamcorper diam. Morbi a porttitor tellus. Fusce viverra
|
</h1>
|
||||||
euismod laoreet. Cras id sapien quis orci rutrum lacinia. Donec
|
<p class="text-base/6">
|
||||||
vitae libero at felis auctor blandit commodo sed libero. Aliquam
|
NR 18 PEMT capacita operadores de plataformas elevatórias para
|
||||||
tellus risus, sagittis a libero eget, hendrerit feugiat mauris.
|
trabalhos em altura com segurança. Com foco na manutenção, inspeção e
|
||||||
Ut vehicula id est non iaculis. Suspendisse potenti. Maecenas in
|
uso correto dos EPIs, previne sempre acidentes, garante certificação
|
||||||
tellus risus. Proin quis libero et ero s ullamcorper faucibus
|
MTE e valoriza sua carreira.
|
||||||
non vitae lacus. Vestibulum at ultricies sem, vel euismod dolor.
|
|
||||||
</p><p>
|
|
||||||
In tempor vel felis ut imperdiet. Pellentesque ac vulputate
|
|
||||||
lorem, id pellentesque velit. Sed rutrum, nisi vel convallis
|
|
||||||
rhoncus, ex mi vulputate leo, id hendrerit ipsum sapien in
|
|
||||||
dolor. Nullam auctor eu nunc sed euismod. Donec molestie velit
|
|
||||||
nec est bibendum pulvinar. Nullam mattis mollis neque, nec
|
|
||||||
cursus mi iaculis et. Morbi tempus purus sit amet orci pulvinar
|
|
||||||
accumsan. Fusce mattis, nisl ac fringilla euismod, orci odio
|
|
||||||
condimentum sapien, a convallis lacus diam et libero. Nunc non
|
|
||||||
urna a orci eleifend porttitor in eget nisi. Ut scelerisque
|
|
||||||
egestas hendrerit. Aenean in tortor cursus, lobortis dolor
|
|
||||||
iaculis, dignissim velit. Nulla facilisi.
|
|
||||||
</p>
|
</p>
|
||||||
|
<ul class="lg:flex gap-3">
|
||||||
|
<li class="flex gap-1">
|
||||||
|
<ClockIcon className="w-5" />
|
||||||
|
<span>Carga horária de 40 horas</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="flex gap-1">
|
||||||
|
<CheckBadgeIcon className="w-5 fill-blue-400" />
|
||||||
|
<span>Certificado com assinatura digital</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex max-lg:flex-col justify-center gap-2.5 lg:gap-8 lg:mt-16"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
class="text-black font-semibold bg-lime-400 rounded p-3.5 hover:bg-white max-lg:text-center"
|
||||||
|
>
|
||||||
|
Contratar agora
|
||||||
|
</a>
|
||||||
|
<a href="http://bit.ly/3RlROu6" class="flex flex-col">
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<span class="font-bold">4.7</span>
|
||||||
|
<ul class="flex">
|
||||||
|
<li>
|
||||||
|
<StarIcon className="w-4 text-yellow-500 fill-yellow-500" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StarIcon className="w-4 text-yellow-500 fill-yellow-500" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StarIcon className="w-4 text-yellow-500 fill-yellow-500" />
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<StarIcon className="w-4 text-yellow-500 fill-yellow-500" />
|
||||||
|
</li>
|
||||||
|
<li><StarIcon className="w-4" /></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span>66 avaliações no Google</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Container className="h-full">
|
||||||
|
<div class="border border-lime-400 rounded-2xl grid grid-cols-3">
|
||||||
|
<div class="bg-lime-300 rounded-2xl p-6 relative h-[36rem]">
|
||||||
|
<Image
|
||||||
|
alt="Homem de negócios"
|
||||||
|
src={homemdenegocios}
|
||||||
|
class="absolute bottom-0 -left-32"
|
||||||
|
/>
|
||||||
|
<Image
|
||||||
|
alt="Mulher de negócios"
|
||||||
|
src={mulherdenegocios}
|
||||||
|
class="absolute bottom-0 -right-26"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-span-2 flex items-center">
|
||||||
|
<div class="w-7/12 mx-auto space-y-5 p-6">
|
||||||
|
<h2 class="text-4xl font-semibold">
|
||||||
|
Por que capacitar sua equipe com a EDUSEG®
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Nós cuidamos da burocracia, oferecemos uma plataforma completa para
|
||||||
|
simplicar a gestão e capacitação em massa de seus colaboradores. Com
|
||||||
|
a EDUSEG®, sua empresa se beneficia de uma tecnologia eficiente e
|
||||||
|
confiável.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<ul class="grid grid-cols-2 gap-2.5">
|
||||||
|
<li class="bg-white/10 p-5 rounded-lg">
|
||||||
|
Conformidade legal garantida
|
||||||
|
</li>
|
||||||
|
<li class="bg-white/10 p-5 rounded-lg">
|
||||||
|
Economia de tempo e recursos
|
||||||
|
</li>
|
||||||
|
<li class="bg-white/10 p-5 rounded-lg">
|
||||||
|
Relatórios e monitoramento
|
||||||
|
</li>
|
||||||
|
<li class="bg-white/10 p-5 rounded-lg">
|
||||||
|
Suporte especializado para gestores
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Container
|
||||||
|
className="py-3 lg:py-12 grid gap-2.5 lg:grid-cols-3 lg:gap-5 lg:w-3/6"
|
||||||
|
>
|
||||||
|
<div class="space-y-2.5">
|
||||||
|
<h1 class="text-4xl lg:text-5xl text-pretty">Módulos deste curso</h1>
|
||||||
|
<p class="text-base/6">
|
||||||
|
O curso é dividido em módulos para facilitar seu aprendizado e garantir
|
||||||
|
que você domine todos os aspectos teóricos e práticos.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="lg:col-span-2 flex flex-col gap-1.5">
|
||||||
|
<Example client:load />
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Container className="py-6 hidden">
|
||||||
|
<ul class="flex gap-2.5">
|
||||||
|
<li class="flex items-center gap-2.5">
|
||||||
|
<span class="bg-white/10 p-2 rounded">
|
||||||
|
<BanknotesIcon className="w-6" />
|
||||||
|
</span>
|
||||||
|
<span class="text-base/5">Economize até 70% com o curso online</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center gap-2.5">
|
||||||
|
<span class="bg-white/10 p-2 rounded">
|
||||||
|
<AcademicCapIcon className="w-6" />
|
||||||
|
</span>
|
||||||
|
<span class="text-base/6"
|
||||||
|
>Certificado digital reconhecido em até 24 horas</span
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center gap-2.5">
|
||||||
|
<span class="bg-white/10 p-2 rounded">
|
||||||
|
<GlobeAmericasIcon className="w-6" />
|
||||||
|
</span>
|
||||||
|
<span>Aprenda no seu tempo e de qualquer lugar</span>
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center gap-2.5">
|
||||||
|
<span class="bg-white/10 p-2 rounded">
|
||||||
|
<PhoneIcon className="w-6" />
|
||||||
|
</span>
|
||||||
|
<span>Suporte de especialistas sempre que precisar</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Container>
|
||||||
|
|
||||||
|
<Container className="hidden">
|
||||||
|
<h2>Quem é o instrutor?</h2>
|
||||||
|
</Container>
|
||||||
|
<!--
|
||||||
|
<Container className="lg:space-y-5 lg:p-3">
|
||||||
|
<Card className="space-y-2.5">
|
||||||
|
<h1 class="text-2xl font-medium">
|
||||||
|
Garanta a capacitação para sua empresa
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Pagamento flexível</li>
|
||||||
|
<li>Acesso imediato</li>
|
||||||
|
<li>Carga horária</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Economize até 70% com o curso online</li>
|
||||||
|
<li>Certificado Digital reconhecido em até 24 Horas</li>
|
||||||
|
<li>Aprenda no seu tempo e de qualquer lugar</li>
|
||||||
|
<li>Suporte de especialistas sempre que precisar</li>
|
||||||
|
</ul>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card color="darker">
|
<section class="lg:grid grid-cols-4">
|
||||||
<p>
|
<div>
|
||||||
In tempor vel felis ut imperdiet. Pellentesque ac vulputate
|
<h1 class="text-2xl font-medium">Módulos deste treinamento</h1>
|
||||||
lorem, id pellentesque velit. Sed rutrum, nisi vel convallis
|
<div>
|
||||||
rhoncus, ex mi vulputate leo, id hendrerit ipsum sapien in
|
O curso é dividido em módulos para facilitar seu aprendizado
|
||||||
dolor. Nullam auctor eu nunc sed euismod. Donec molestie velit
|
e garantir que você domine todos os aspectos teóricos e
|
||||||
nec est bibendum pulvinar. Nullam mattis mollis neque, nec
|
práticos.
|
||||||
cursus mi iaculis et. Morbi tempus purus sit amet orci pulvinar
|
</div>
|
||||||
accumsan. Fusce mattis, nisl ac fringilla euismod, orci odio
|
</div>
|
||||||
condimentum sapien, a convallis lacus diam et libero. Nunc non
|
<div><Example client:load /></div>
|
||||||
urna a orci eleifend porttitor in eget nisi. Ut scelerisque
|
</section>
|
||||||
egestas hendrerit. Aenean in tortor cursus, lobortis dolor
|
</Container>
|
||||||
iaculis, dignissim velit. Nulla facilisi.
|
|
||||||
</p>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<section class="grid lg:grid-cols-2 gap-6 p-3">
|
<section class="grid lg:grid-cols-2 gap-6 p-3">
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<h4 class="font-medium text-4xl">Solicite um orçamento</h4>
|
<h4 class="font-medium text-4xl">Solicite um orçamento</h4>
|
||||||
<p>
|
<p>
|
||||||
Quer saber como podemos capacitar sua equipe?<br
|
Quer saber como podemos capacitar sua equipe?
|
||||||
class="max-lg:hidden"
|
<br class="max-lg:hidden" />
|
||||||
/>
|
|
||||||
Fale com nossa equipe e receba uma proposta personalizada.
|
Fale com nossa equipe e receba uma proposta personalizada.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -73,5 +230,5 @@ import Layout from "@layouts/Layout.astro";
|
|||||||
<Form client:load />
|
<Form client:load />
|
||||||
</Card>
|
</Card>
|
||||||
</section>
|
</section>
|
||||||
</Container>
|
-->
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
BIN
superpage/src/pages/mulher-de-negocios.png
Normal file
BIN
superpage/src/pages/mulher-de-negocios.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 256 KiB |
BIN
superpage/src/pages/nr18-plataforma.png
Normal file
BIN
superpage/src/pages/nr18-plataforma.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 948 KiB |
Reference in New Issue
Block a user