we getting there...
This commit is contained in:
parent
9011c9f34d
commit
305a58e8b5
45 changed files with 760 additions and 85 deletions
12
app-template-ui/.neoconf.json
Normal file
12
app-template-ui/.neoconf.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"lspconfig": {
|
||||||
|
"rust_analyzer": {
|
||||||
|
"rust-analyzer.cargo.features": "all",
|
||||||
|
"rust-analyzer.rustfmt.overrideCommand": [
|
||||||
|
"leptosfmt",
|
||||||
|
"--stdin",
|
||||||
|
"--rustfmt"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ crate-type = ["cdylib", "rlib"]
|
||||||
axum = { workspace = true, optional = true }
|
axum = { workspace = true, optional = true }
|
||||||
console_error_panic_hook = { workspace = true }
|
console_error_panic_hook = { workspace = true }
|
||||||
leptos = { workspace = true, features = ["nightly"] }
|
leptos = { workspace = true, features = ["nightly"] }
|
||||||
|
anyhow = { workspace = true, optional = true }
|
||||||
leptos_axum = { workspace = true, optional = true }
|
leptos_axum = { workspace = true, optional = true }
|
||||||
leptos_meta = { workspace = true, features = ["nightly"] }
|
leptos_meta = { workspace = true, features = ["nightly"] }
|
||||||
leptos_router = { workspace = true, features = ["nightly"] }
|
leptos_router = { workspace = true, features = ["nightly"] }
|
||||||
|
@ -29,13 +30,20 @@ tower = { workspace = true, optional = true }
|
||||||
tower-http = { workspace = true, features = ["fs"], optional = true }
|
tower-http = { workspace = true, features = ["fs"], optional = true }
|
||||||
wasm-bindgen = { workspace = true }
|
wasm-bindgen = { workspace = true }
|
||||||
tracing = { workspace = true, optional = true }
|
tracing = { workspace = true, optional = true }
|
||||||
|
tracing-subscriber = { workspace = true, optional = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
http = { workspace = true }
|
http = { workspace = true }
|
||||||
pulldown-cmark = { workspace = true }
|
pulldown-cmark = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
saleor-app-sdk = { workspace = true }
|
saleor-app-sdk = { workspace = true, features = ["bridge"], optional = true }
|
||||||
dotenvy = { workspace = true }
|
dotenvy = { workspace = true }
|
||||||
|
envy = { workspace = true }
|
||||||
|
cynic = { workspace = true, features = ["http-surf"], optional = true }
|
||||||
|
surf = { workspace = true, optional = true }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cynic-codegen = { workspace = true, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
hydrate = ["leptos/hydrate", "leptos_meta/hydrate", "leptos_router/hydrate"]
|
||||||
|
@ -45,12 +53,19 @@ ssr = [
|
||||||
"dep:tower",
|
"dep:tower",
|
||||||
"dep:tower-http",
|
"dep:tower-http",
|
||||||
"dep:leptos_axum",
|
"dep:leptos_axum",
|
||||||
|
"dep:tracing",
|
||||||
|
"dep:saleor-app-sdk",
|
||||||
|
"dep:tracing-subscriber",
|
||||||
|
"dep:anyhow",
|
||||||
|
"dep:cynic",
|
||||||
|
"dep:cynic-codegen",
|
||||||
|
"dep:surf",
|
||||||
"leptos/ssr",
|
"leptos/ssr",
|
||||||
"leptos_meta/ssr",
|
"leptos_meta/ssr",
|
||||||
"leptos_router/ssr",
|
"leptos_router/ssr",
|
||||||
"dep:tracing",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# Defines a size-optimized profile for the WASM bundle in release mode
|
# Defines a size-optimized profile for the WASM bundle in release mode
|
||||||
[profile.wasm-release]
|
[profile.wasm-release]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
|
|
11
app-template-ui/build.rs
Normal file
11
app-template-ui/build.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
fn main() {
|
||||||
|
cynic_codegen::register_schema("saleor")
|
||||||
|
.from_sdl_file("../schema.graphql")
|
||||||
|
.unwrap()
|
||||||
|
.as_default()
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
fn main() {}
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 264 KiB |
BIN
app-template-ui/public/fonts/Inter-Black.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-Black.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-BlackItalic.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-BlackItalic.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-Bold.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-Bold.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-BoldItalic.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-ExtraBold.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-ExtraBold.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-ExtraBoldItalic.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-ExtraBoldItalic.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-ExtraLight.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-ExtraLight.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-ExtraLightItalic.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-ExtraLightItalic.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-Italic.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-Italic.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-Light.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-Light.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-LightItalic.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-LightItalic.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-Medium.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-Medium.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-MediumItalic.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-MediumItalic.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-Regular.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-Regular.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-SemiBold.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-SemiBold.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-SemiBoldItalic.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-SemiBoldItalic.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-Thin.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-Thin.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-ThinItalic.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-ThinItalic.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-italic.var.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-italic.var.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter-roman.var.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter-roman.var.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/fonts/Inter.var.woff2
Normal file
BIN
app-template-ui/public/fonts/Inter.var.woff2
Normal file
Binary file not shown.
BIN
app-template-ui/public/logo.png
Normal file
BIN
app-template-ui/public/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
5
app-template-ui/rust-toolchain.toml
Normal file
5
app-template-ui/rust-toolchain.toml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly-2024-04-28"
|
||||||
|
## Toggle to this one for sdk releases
|
||||||
|
# channel = "stable"
|
||||||
|
targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
|
|
@ -15,10 +15,10 @@ pub fn App() -> impl IntoView {
|
||||||
provide_meta_context();
|
provide_meta_context();
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<Stylesheet id="leptos" href="/pkg/saleor-marketplace.css"/>
|
<Stylesheet id="leptos" href="/pkg/saleor-app-template-ui.css"/>
|
||||||
|
|
||||||
// sets the document title
|
// sets the document title
|
||||||
<Title text="Saleors Harbour"/>
|
<Title text="Example UI App template in Rust"/>
|
||||||
|
|
||||||
// content for this welcome page
|
// content for this welcome page
|
||||||
<Router fallback=|| {
|
<Router fallback=|| {
|
||||||
|
@ -28,9 +28,18 @@ pub fn App() -> impl IntoView {
|
||||||
}>
|
}>
|
||||||
<main class="p-4 md:p-8 md:px-16">
|
<main class="p-4 md:p-8 md:px-16">
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="" view=Home/>
|
<Route path="/" view=Home/>
|
||||||
</Routes>
|
</Routes>
|
||||||
</main>
|
</main>
|
||||||
</Router>
|
</Router>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AppState {
|
||||||
|
pub saleor_app: std::sync::Arc<tokio::sync::Mutex<saleor_app_sdk::SaleorApp>>,
|
||||||
|
pub config: saleor_app_sdk::config::Config,
|
||||||
|
pub manifest: saleor_app_sdk::manifest::AppManifest,
|
||||||
|
pub leptos_options: LeptosOptions,
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
use leptos::*;
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
pub mod app_editor;
|
|
||||||
|
|
|
@ -1,7 +1,41 @@
|
||||||
use http::status::StatusCode;
|
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
use axum::response::{IntoResponse, Response};
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
use http::header::ToStrError;
|
||||||
|
use http::status::StatusCode;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/* ERROR STUFF FOR AXUM */
|
||||||
|
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum AxumError {
|
||||||
|
#[error("Error converting something to string, `{0}`")]
|
||||||
|
ToStrError(#[from] ToStrError),
|
||||||
|
#[error("Anyhow function error: {0}")]
|
||||||
|
Anyhow(#[from] anyhow::Error),
|
||||||
|
#[error("Request is missing header `{0}`")]
|
||||||
|
MissingHeader(String),
|
||||||
|
#[error("Internal server error, `{0}`")]
|
||||||
|
InternalServerError(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell axum how to convert `AppError` into a response.
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
impl IntoResponse for AxumError {
|
||||||
|
fn into_response(self) -> Response {
|
||||||
|
(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("Something went wrong: {:?}", self),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* THIS IS ERROR STUFF FOR LEPTOS */
|
||||||
|
|
||||||
#[derive(Clone, Debug, Error)]
|
#[derive(Clone, Debug, Error)]
|
||||||
pub enum AppError {
|
pub enum AppError {
|
||||||
#[error("Not Found")]
|
#[error("Not Found")]
|
||||||
|
@ -52,16 +86,16 @@ pub fn ErrorTemplate(
|
||||||
}
|
}
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<h1>{if errors.len() > 1 {"Errors"} else {"Error"}}</h1>
|
<h1>{if errors.len() > 1 { "Errors" } else { "Error" }}</h1>
|
||||||
<For
|
<For
|
||||||
// a function that returns the items we're iterating over; a signal is fine
|
// a function that returns the items we're iterating over; a signal is fine
|
||||||
each= move || {errors.clone().into_iter().enumerate()}
|
each=move || { errors.clone().into_iter().enumerate() }
|
||||||
// a unique key for each item as a reference
|
// a unique key for each item as a reference
|
||||||
key=|(index, _error)| *index
|
key=|(index, _error)| *index
|
||||||
// renders each item to a view
|
// renders each item to a view
|
||||||
children=move |error| {
|
children=move |error| {
|
||||||
let error_string = error.1.to_string();
|
let error_string = error.1.to_string();
|
||||||
let error_code= error.1.status_code();
|
let error_code = error.1.status_code();
|
||||||
view! {
|
view! {
|
||||||
<h2>{error_code.to_string()}</h2>
|
<h2>{error_code.to_string()}</h2>
|
||||||
<p>"Error: " {error_string}</p>
|
<p>"Error: " {error_string}</p>
|
||||||
|
|
|
@ -2,10 +2,11 @@ pub mod app;
|
||||||
pub mod error_template;
|
pub mod error_template;
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
pub mod fileserv;
|
pub mod fileserv;
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
pub mod queries;
|
||||||
|
|
||||||
pub mod components;
|
pub mod components;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
pub mod server;
|
|
||||||
|
|
||||||
#[cfg(feature = "hydrate")]
|
#[cfg(feature = "hydrate")]
|
||||||
#[wasm_bindgen::prelude::wasm_bindgen]
|
#[wasm_bindgen::prelude::wasm_bindgen]
|
||||||
|
|
|
@ -1,44 +1,133 @@
|
||||||
pub mod server;
|
#![allow(
|
||||||
pub mod components;
|
non_upper_case_globals,
|
||||||
pub mod routes;
|
clippy::large_enum_variant,
|
||||||
|
clippy::upper_case_acronyms,
|
||||||
|
dead_code
|
||||||
|
)]
|
||||||
|
#![feature(let_chains)]
|
||||||
|
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
pub mod fileserv;
|
mod fileserv;
|
||||||
#[cfg(feature = "ssr")]
|
#[cfg(feature = "ssr")]
|
||||||
pub mod error_template;
|
mod queries;
|
||||||
|
|
||||||
|
mod app;
|
||||||
|
mod components;
|
||||||
|
mod routes;
|
||||||
|
mod error_template;
|
||||||
|
|
||||||
#[cfg(feature = "ssr")]
|
|
||||||
pub mod app;
|
|
||||||
#[cfg(feature = "ssr")]
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
#[cfg(feature = "ssr")]
|
||||||
|
async fn main() -> Result<(), std::io::Error> {
|
||||||
|
|
||||||
use axum::Router;
|
use std::sync::Arc;
|
||||||
|
use axum::{middleware, routing::{post,get}, Router};
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_axum::{generate_route_list, LeptosRoutes};
|
use leptos_axum::{generate_route_list, LeptosRoutes};
|
||||||
use crate::app::*;
|
use app::*;
|
||||||
use crate::fileserv::file_and_error_handler;
|
use fileserv::file_and_error_handler;
|
||||||
// Setting get_configuration(None) means we'll be using cargo-leptos's env values
|
use saleor_app_sdk::middleware::verify_webhook_signature::webhook_signature_verifier;
|
||||||
// For deployment these variables are:
|
use tokio::sync::Mutex;
|
||||||
// <https://github.com/leptos-rs/start-axum#executing-a-server-on-a-remote-machine-without-the-toolchain>
|
use saleor_app_sdk::{
|
||||||
// Alternately a file can be specified such as Some("Cargo.toml")
|
cargo_info,
|
||||||
// The file would need to be included with the executable when moved to deployment
|
config::Config,
|
||||||
|
manifest::{AppManifestBuilder, AppPermission},
|
||||||
|
webhooks::{AsyncWebhookEventType, WebhookManifestBuilder},
|
||||||
|
SaleorApp,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::routes::api::{manifest::manifest, register::register, webhooks::webhooks};
|
||||||
|
|
||||||
|
//Leptos stuff
|
||||||
let conf = get_configuration(None).await.unwrap();
|
let conf = get_configuration(None).await.unwrap();
|
||||||
let leptos_options = conf.leptos_options;
|
let leptos_options = conf.leptos_options;
|
||||||
let addr = leptos_options.site_addr;
|
|
||||||
let routes = generate_route_list(App);
|
let routes = generate_route_list(App);
|
||||||
|
|
||||||
// build our application with a route
|
// Saleor stuff
|
||||||
let app = Router::new()
|
let config = Config::load().unwrap();
|
||||||
|
trace_to_std(&config).unwrap();
|
||||||
|
let saleor_app = SaleorApp::new(&config).unwrap();
|
||||||
|
|
||||||
|
let app_manifest = AppManifestBuilder::new(&config, cargo_info!())
|
||||||
|
.add_webhook(
|
||||||
|
WebhookManifestBuilder::new(&config)
|
||||||
|
.set_query(
|
||||||
|
r#"
|
||||||
|
subscription QueryProductsChanged {
|
||||||
|
event {
|
||||||
|
... on ProductUpdated {
|
||||||
|
product {
|
||||||
|
... BaseProduct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on ProductCreated {
|
||||||
|
product {
|
||||||
|
... BaseProduct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on ProductDeleted {
|
||||||
|
product {
|
||||||
|
... BaseProduct
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment BaseProduct on Product {
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
name
|
||||||
|
category {
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.add_async_events(vec![
|
||||||
|
AsyncWebhookEventType::ProductCreated,
|
||||||
|
AsyncWebhookEventType::ProductUpdated,
|
||||||
|
AsyncWebhookEventType::ProductDeleted,
|
||||||
|
])
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.add_permission(AppPermission::ManageProducts)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let app_state = AppState{
|
||||||
|
manifest: app_manifest,
|
||||||
|
config: config.clone(),
|
||||||
|
saleor_app: Arc::new(Mutex::new(saleor_app)),
|
||||||
|
leptos_options,
|
||||||
|
};
|
||||||
|
|
||||||
|
let state_1 = app_state.clone();
|
||||||
|
let app =
|
||||||
|
Router::new()
|
||||||
|
.layer(middleware::from_fn(webhook_signature_verifier))
|
||||||
|
.route("/api/webhooks", post(webhooks))
|
||||||
|
.route("/api/register", post(register))
|
||||||
|
.route("/api/manifest", get(manifest))
|
||||||
|
// .leptos_routes_with_context(&leptos_options, routes,move || provide_context(state_1.clone()) , App)
|
||||||
.leptos_routes(&leptos_options, routes, App)
|
.leptos_routes(&leptos_options, routes, App)
|
||||||
.fallback(file_and_error_handler)
|
.fallback(file_and_error_handler)
|
||||||
.with_state(leptos_options);
|
.with_state(app_state.clone());
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
|
||||||
logging::log!("listening on http://{}", &addr);
|
let listener = tokio::net::TcpListener::bind(
|
||||||
axum::serve(listener, app.into_make_service())
|
"0.0.0.0:".to_owned()
|
||||||
.await
|
+ config
|
||||||
.unwrap();
|
.app_api_base_url
|
||||||
|
.split(':')
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.get(2)
|
||||||
|
.unwrap_or(&"3000"),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
tracing::debug!("listening on {}", listener.local_addr()?);
|
||||||
|
|
||||||
|
let _= axum::serve(listener, app.into_make_service())
|
||||||
|
.await;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "ssr"))]
|
#[cfg(not(feature = "ssr"))]
|
||||||
|
@ -47,3 +136,26 @@ pub fn main() {
|
||||||
// unless we want this to work with e.g., Trunk for a purely client-side app
|
// unless we want this to work with e.g., Trunk for a purely client-side app
|
||||||
// see lib.rs for hydration function instead
|
// see lib.rs for hydration function instead
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
use saleor_app_sdk::config::Config;
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
pub fn trace_to_std(config: &Config) -> Result<(), envy::Error> {
|
||||||
|
use tracing::level_filters::LevelFilter;
|
||||||
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
|
let filter = EnvFilter::builder()
|
||||||
|
.with_default_directive(LevelFilter::DEBUG.into())
|
||||||
|
.from_env().unwrap()
|
||||||
|
.add_directive(format!("{}={}", env!("CARGO_PKG_NAME"), config.log_level).parse().unwrap());
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_max_level(config.log_level)
|
||||||
|
.with_env_filter(filter)
|
||||||
|
.with_target(true)
|
||||||
|
.compact()
|
||||||
|
.init();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
45
app-template-ui/src/queries/event_products_updated.rs
Normal file
45
app-template-ui/src/queries/event_products_updated.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#[cynic::schema("saleor")]
|
||||||
|
mod schema {}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
#[cynic(graphql_type = "Subscription")]
|
||||||
|
pub struct QueryProductsChanged {
|
||||||
|
pub event: Option<Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct ProductUpdated {
|
||||||
|
pub product: Option<Product>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct ProductDeleted {
|
||||||
|
pub product: Option<Product>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct ProductCreated {
|
||||||
|
pub product: Option<Product>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct Product {
|
||||||
|
pub id: cynic::Id,
|
||||||
|
pub slug: String,
|
||||||
|
pub name: String,
|
||||||
|
pub category: Option<Category>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct Category {
|
||||||
|
pub slug: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::InlineFragments, Debug)]
|
||||||
|
pub enum Event {
|
||||||
|
ProductUpdated(ProductUpdated),
|
||||||
|
ProductCreated(ProductCreated),
|
||||||
|
ProductDeleted(ProductDeleted),
|
||||||
|
#[cynic(fallback)]
|
||||||
|
Unknown,
|
||||||
|
}
|
2
app-template-ui/src/queries/mod.rs
Normal file
2
app-template-ui/src/queries/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod event_products_updated;
|
||||||
|
pub mod product_metadata_update;
|
75
app-template-ui/src/queries/product_metadata_update.rs
Normal file
75
app-template-ui/src/queries/product_metadata_update.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#[cynic::schema("saleor")]
|
||||||
|
mod schema {}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryVariables, Debug)]
|
||||||
|
pub struct UpdateProductMetadataVariables<'a> {
|
||||||
|
pub metadata: Option<Vec<MetadataInput<'a>>>,
|
||||||
|
pub product_id: &'a cynic::Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
#[cynic(
|
||||||
|
graphql_type = "Mutation",
|
||||||
|
variables = "UpdateProductMetadataVariables"
|
||||||
|
)]
|
||||||
|
pub struct UpdateProductMetadata {
|
||||||
|
#[arguments(id: $product_id, input: { metadata: $metadata })]
|
||||||
|
pub product_update: Option<ProductUpdate>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct ProductUpdate {
|
||||||
|
pub errors: Vec<ProductError>,
|
||||||
|
pub product: Option<Product>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct Product {
|
||||||
|
pub id: cynic::Id,
|
||||||
|
pub metadata: Vec<MetadataItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct ProductError {
|
||||||
|
pub field: Option<String>,
|
||||||
|
pub message: Option<String>,
|
||||||
|
pub code: ProductErrorCode,
|
||||||
|
pub attributes: Option<Vec<cynic::Id>>,
|
||||||
|
pub values: Option<Vec<cynic::Id>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::QueryFragment, Debug)]
|
||||||
|
pub struct MetadataItem {
|
||||||
|
pub key: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::Enum, Clone, Copy, Debug)]
|
||||||
|
pub enum ProductErrorCode {
|
||||||
|
AlreadyExists,
|
||||||
|
AttributeAlreadyAssigned,
|
||||||
|
AttributeCannotBeAssigned,
|
||||||
|
AttributeVariantsDisabled,
|
||||||
|
MediaAlreadyAssigned,
|
||||||
|
DuplicatedInputItem,
|
||||||
|
GraphqlError,
|
||||||
|
Invalid,
|
||||||
|
InvalidPrice,
|
||||||
|
ProductWithoutCategory,
|
||||||
|
NotProductsImage,
|
||||||
|
NotProductsVariant,
|
||||||
|
NotFound,
|
||||||
|
Required,
|
||||||
|
Unique,
|
||||||
|
VariantNoDigitalContent,
|
||||||
|
CannotManageProductWithoutVariant,
|
||||||
|
ProductNotAssignedToChannel,
|
||||||
|
UnsupportedMediaProvider,
|
||||||
|
PreorderVariantCannotBeDeactivated,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(cynic::InputObject, Debug)]
|
||||||
|
pub struct MetadataInput<'a> {
|
||||||
|
pub key: &'a str,
|
||||||
|
pub value: &'a str,
|
||||||
|
}
|
8
app-template-ui/src/routes/api/manifest.rs
Normal file
8
app-template-ui/src/routes/api/manifest.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
use axum::{extract::State, Json};
|
||||||
|
use saleor_app_sdk::manifest::AppManifest;
|
||||||
|
|
||||||
|
use crate::{app::AppState, error_template::AxumError};
|
||||||
|
|
||||||
|
pub fn manifest(State(state): State<AppState>) -> Result<Json<AppManifest>, AxumError> {
|
||||||
|
Ok(Json(state.manifest))
|
||||||
|
}
|
3
app-template-ui/src/routes/api/mod.rs
Normal file
3
app-template-ui/src/routes/api/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod manifest;
|
||||||
|
pub mod register;
|
||||||
|
pub mod webhooks;
|
41
app-template-ui/src/routes/api/register.rs
Normal file
41
app-template-ui/src/routes/api/register.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
use anyhow::Context;
|
||||||
|
use axum::{
|
||||||
|
extract::Json,
|
||||||
|
extract::State,
|
||||||
|
http::{HeaderMap, StatusCode},
|
||||||
|
};
|
||||||
|
use saleor_app_sdk::{AuthData, AuthToken};
|
||||||
|
use tracing::{debug, info};
|
||||||
|
|
||||||
|
use crate::{app::AppState, error_template::AxumError};
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn register(
|
||||||
|
headers: HeaderMap,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Json(auth_token): Json<AuthToken>,
|
||||||
|
) -> Result<StatusCode, AxumError> {
|
||||||
|
debug!(
|
||||||
|
"/api/register:\nsaleor_api_url:{:?}\nauth_token:{:?}",
|
||||||
|
headers.get("saleor-api-url"),
|
||||||
|
auth_token
|
||||||
|
);
|
||||||
|
|
||||||
|
if auth_token.auth_token.is_empty() {
|
||||||
|
return Err(anyhow::anyhow!("missing auth_token").into());
|
||||||
|
}
|
||||||
|
let app = state.saleor_app.lock().await;
|
||||||
|
let saleor_api_url = headers.get("saleor-api-url").context("missing api field")?;
|
||||||
|
let saleor_api_url = saleor_api_url.to_str()?.to_owned();
|
||||||
|
let auth_data = AuthData {
|
||||||
|
jwks: None,
|
||||||
|
token: auth_token.auth_token,
|
||||||
|
domain: Some(state.config.app_api_base_url),
|
||||||
|
app_id: state.manifest.id,
|
||||||
|
saleor_api_url: saleor_api_url.clone(),
|
||||||
|
};
|
||||||
|
app.apl.set(auth_data).await?;
|
||||||
|
|
||||||
|
info!("registered app for{:?}", &saleor_api_url);
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
70
app-template-ui/src/routes/api/webhooks.rs
Normal file
70
app-template-ui/src/routes/api/webhooks.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use axum::{
|
||||||
|
extract::{Json, State},
|
||||||
|
http::{HeaderMap, StatusCode},
|
||||||
|
};
|
||||||
|
use cynic::{http::SurfExt, MutationBuilder};
|
||||||
|
use saleor_app_sdk::{
|
||||||
|
headers::{SALEOR_API_URL_HEADER,SALEOR_EVENT_HEADER},
|
||||||
|
webhooks::{
|
||||||
|
utils::{get_webhook_event_type, EitherWebhookType},
|
||||||
|
AsyncWebhookEventType,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use tracing::{debug, info};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::AppState, error_template::AxumError, queries::{event_products_updated::ProductUpdated, product_metadata_update::{MetadataInput, UpdateProductMetadata, UpdateProductMetadataVariables}} };
|
||||||
|
|
||||||
|
pub async fn webhooks(
|
||||||
|
headers: HeaderMap,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
//Will try to convert req body to ProductUpdated type, else returns 400
|
||||||
|
Json(product): Json<ProductUpdated>,
|
||||||
|
) -> Result<StatusCode, AxumError> {
|
||||||
|
debug!("/api/webhooks");
|
||||||
|
debug!("req: {:?}", product);
|
||||||
|
debug!("headers: {:?}", headers);
|
||||||
|
|
||||||
|
let url = headers
|
||||||
|
.get(SALEOR_API_URL_HEADER).ok_or(AxumError::MissingHeader(SALEOR_API_URL_HEADER.to_owned()))?;
|
||||||
|
let event_type = get_webhook_event_type(&headers).map_err(|_|AxumError::MissingHeader(SALEOR_EVENT_HEADER.to_owned()))?;
|
||||||
|
if let EitherWebhookType::Async(a) = event_type {
|
||||||
|
match a {
|
||||||
|
AsyncWebhookEventType::ProductUpdated
|
||||||
|
| AsyncWebhookEventType::ProductCreated
|
||||||
|
| AsyncWebhookEventType::ProductDeleted => {
|
||||||
|
update_product(product, url.to_str()?, state).await?
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("got webhooks!");
|
||||||
|
Ok(StatusCode::OK)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_product(
|
||||||
|
product: ProductUpdated,
|
||||||
|
saleor_api_url: &str,
|
||||||
|
state: AppState,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
debug!("Product got changed!");
|
||||||
|
if let Some(product) = product.product {
|
||||||
|
let operation = UpdateProductMetadata::build(UpdateProductMetadataVariables {
|
||||||
|
product_id: &product.id,
|
||||||
|
metadata: Some(vec![MetadataInput {
|
||||||
|
key: "helloloo",
|
||||||
|
value: "hiiiihii",
|
||||||
|
}]),
|
||||||
|
});
|
||||||
|
let saleor_app = state.saleor_app.lock().await;
|
||||||
|
let auth_data = saleor_app.apl.get(saleor_api_url).await?;
|
||||||
|
let result = surf::post(saleor_api_url)
|
||||||
|
.header("Authorization", format!("bearer {}", auth_data.token))
|
||||||
|
.run_graphql(operation)
|
||||||
|
.await;
|
||||||
|
debug!("update product result : {:?}", result);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -1 +1,3 @@
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
pub mod api;
|
||||||
pub mod home;
|
pub mod home;
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
use leptos::*;
|
|
|
@ -1 +0,0 @@
|
||||||
pub mod functions;
|
|
|
@ -5,16 +5,18 @@
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
|
@supports (font-variation-settings: normal) {
|
||||||
body {
|
body {
|
||||||
@apply bg-brand-white;
|
font-family: "'Inter var',sans-serif";
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
|
||||||
@apply text-brand-black;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection {
|
::selection {
|
||||||
@apply bg-brand-sunset-400;
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@apply font-serif text-default1 bg-default1;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
|
@ -23,22 +25,22 @@
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
@apply font-sans my-4 text-brand-sea-300 text-2xl;
|
@apply my-4 text-2xl text-default1;
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
@apply text-brand-sea-300 text-xl;
|
@apply text-xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3,
|
h3,
|
||||||
h4,
|
h4,
|
||||||
h5,
|
h5,
|
||||||
h6 {
|
h6 {
|
||||||
@apply text-brand-sea-500 text-base;
|
@apply text-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@apply font-serif my-2 text-base;
|
@apply my-2 text-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:disabled,
|
input:disabled,
|
||||||
|
@ -66,13 +68,202 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer components {
|
@font-face {
|
||||||
.header-gradient {
|
font-family: "Inter";
|
||||||
background: theme("colors.brand-sea.400");
|
src:
|
||||||
background: linear-gradient(25deg,
|
local("Inter"),
|
||||||
theme("colors.brand-sunset.500") 0%,
|
url("/fonts/Inter-Thin.woff2") format("woff2");
|
||||||
theme("colors.brand-sea.300") 45%,
|
font-display: swap;
|
||||||
theme("colors.brand-sea.300") 55%,
|
font-style: normal;
|
||||||
theme("colors.brand-sunset.500") 100%);
|
font-weight: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-ThinItalic.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-ExtraLight.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-ExtraLightItalic.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-Light.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-LightItalic.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-Regular.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-Italic.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-Medium.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-MediumItalic.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-SemiBold.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-SemiBoldItalic.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-Bold.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-BoldItalic.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-ExtraBold.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-ExtraBoldItalic.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-Black.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-BlackItalic.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter var";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-roman.var.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inter var";
|
||||||
|
src:
|
||||||
|
local("Inter"),
|
||||||
|
url("/fonts/Inter-italic.var.woff2") format("woff2");
|
||||||
|
font-display: swap;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 100 900;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/** STYLES TAKEN PARTIALLY FROM SALEORS MACAW-UI**/
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: {
|
content: {
|
||||||
|
@ -5,8 +6,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
theme: {
|
theme: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ["Space Grotesk", "sans-serif"],
|
serif: ["Inter", "sans-serif"],
|
||||||
serif: ["PT Serif", "serif"],
|
|
||||||
},
|
},
|
||||||
fontSize: {
|
fontSize: {
|
||||||
xs: "0.75rem",
|
xs: "0.75rem",
|
||||||
|
@ -26,30 +26,71 @@ module.exports = {
|
||||||
max: "999999px",
|
max: "999999px",
|
||||||
},
|
},
|
||||||
extend: {
|
extend: {
|
||||||
|
backgroundColor: {
|
||||||
|
accent1: "hsla(215, 100%, 62%, 1)",
|
||||||
|
accent1Hovered: "hsla(215, 100%, 51%, 0.16)",
|
||||||
|
accent1Pressed: "hsla(215, 100%, 51%, 0.32)",
|
||||||
|
buttonCriticalDisabled: "hsla(204, 16%, 94%, 1)",
|
||||||
|
buttonCriticalPrimary: "hsla(11, 100%, 56%, 1)",
|
||||||
|
buttonCriticalPrimaryFocused: "hsla(11, 100%, 42%, 1)",
|
||||||
|
buttonCriticalPrimaryHovered: "hsla(11, 100%, 42%, 1)",
|
||||||
|
buttonCriticalPrimaryPressed: "hsla(11, 100%, 29%, 1)",
|
||||||
|
buttonDefaultDisabled: "hsla(211, 32%, 21%, 1)",
|
||||||
|
buttonDefaultPrimary: "hsla(0, 0%, 100%, 1)",
|
||||||
|
buttonDefaultPrimaryFocused: "hsla(210, 24%, 86%, 1)",
|
||||||
|
buttonDefaultPrimaryHovered: "hsla(211, 24%, 86%, 1)",
|
||||||
|
buttonDefaultPrimaryPressed: "hsla(211, 16%, 68%, 1)",
|
||||||
|
buttonDefaultSecondary: "hsla(232, 17%, 18%, 1)",
|
||||||
|
buttonDefaultSecondaryFocused: "hsla(211, 32%, 19%, 1)",
|
||||||
|
buttonDefaultSecondaryHovered: "hsla(211, 32%, 19%, 1)",
|
||||||
|
buttonDefaultSecondaryPressed: "hsla(211, 24%, 26%, 1)",
|
||||||
|
buttonDefaultTertiary: "hsla(180, 4%, 15%, 0)",
|
||||||
|
buttonDefaultTertiaryFocused: "hsla(0, 0%, 100%, 0.06)",
|
||||||
|
buttonDefaultTertiaryHovered: "hsla(0, 0%, 100%, 0.06)",
|
||||||
|
buttonDefaultTertiaryPressed: "hsla(0, 0%, 100%, 0.12)",
|
||||||
|
critical1: "hsla(11, 100%, 96%, 1)",
|
||||||
|
critical1Focused: "hsla(11, 100%, 46%, 0.2)",
|
||||||
|
critical1Hovered: "hsla(11, 100%, 46%, 0.2)",
|
||||||
|
critical1Pressed: "hsla(11, 100%, 46%, 0.32)",
|
||||||
|
critical2: "hsla(11, 100%, 56%, 1)",
|
||||||
|
default1: "hsla(232, 17%, 18%, 1)",
|
||||||
|
default1Focused: "hsla(0, 0%, 100%, 0.06)",
|
||||||
|
default1Hovered: "hsla(0, 0%, 100%, 0.06)",
|
||||||
|
default1Pressed: "hsla(0, 0%, 100%, 0.12)",
|
||||||
|
default2: "hsla(231, 17%, 16%, 1)",
|
||||||
|
default3: "hsla(211, 42%, 12%, 1)",
|
||||||
|
defaultDisabled: "hsla(211, 32%, 21%, 1)",
|
||||||
|
info1: "hsla(215, 100%, 62%, 1)",
|
||||||
|
success1: "hsla(173, 100%, 32%, 1)",
|
||||||
|
warning1: "hsla(42, 100%, 84%, 1)",
|
||||||
|
},
|
||||||
|
borderColor: {
|
||||||
|
accent1: "hsla(215, 100%, 39%, 1)",
|
||||||
|
critical1: "hsla(11, 100%, 35%, 1)",
|
||||||
|
default1: "hsla(210, 32%, 25%, 1)",
|
||||||
|
default1Focused: "hsla(212, 24%, 32%, 1)",
|
||||||
|
default1Hovered: "hsla(210, 32%, 25%, 1)",
|
||||||
|
defaultDisabled: "hsla(231, 18%, 23%, 1)",
|
||||||
|
default2: "hsla(211, 21%, 39%, 1)",
|
||||||
|
info1: "hsla(210, 32%, 25%, 1)",
|
||||||
|
success1: "hsl(173, 79%, 62%, 1)",
|
||||||
|
warning1: "hsla(36, 44%, 50%, 1)",
|
||||||
|
},
|
||||||
colors: {
|
colors: {
|
||||||
"brand-sea": {
|
accent1: "hsla(215, 100%, 83%, 1)",
|
||||||
100: "#C5ECE0",
|
buttonCriticalDisabled: "hsla(212, 14%, 67%, 1)",
|
||||||
200: "#17C3B2",
|
buttonCriticalPrimary: "hsla(0, 0%, 100%, 1)",
|
||||||
300: "#1DA0A8",
|
buttonDefaultPrimary: "hsla(212, 44%, 13%, 1)",
|
||||||
400: "#227C9D",
|
buttonDefaultSecondary: "hsla(0, 0%, 100%, 1)",
|
||||||
500: "#094074",
|
buttonDefaultTertiary: "hsla(0, 0%, 100%, 1)",
|
||||||
},
|
critical1: "hsla(11, 100%, 82%, 1)",
|
||||||
"brand-sunset": {
|
critical2: "hsla(11, 100%, 58%, 1)",
|
||||||
100: "#FEF9EF",
|
default1: "hsla(0, 0%, 100%, 1)",
|
||||||
200: "#FFEED1",
|
default2: "hsla(230, 10%, 53%, 1)",
|
||||||
300: "#FFE2B3",
|
defaultDisabled: "hsla(212, 19%, 39%, 1)",
|
||||||
400: "#FFD795",
|
info1: "hsla(215, 100%, 83%, 1)",
|
||||||
500: "#FFCB77",
|
success1: "hsla(173, 79%, 62%, 1)",
|
||||||
},
|
warning1: "hsla(36, 44%, 50%, 1)",
|
||||||
"brand-red": {
|
|
||||||
100: "#FED6D0",
|
|
||||||
200: "#FEB3B1",
|
|
||||||
300: "#FE9092",
|
|
||||||
400: "#FE7F83",
|
|
||||||
500: "#FE6D73",
|
|
||||||
},
|
|
||||||
"brand-black": "#161a1e",
|
|
||||||
"brand-white": "#fbfbfb",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue