diff --git a/.gitignore b/.gitignore index d1caaf3..1bfc6ae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ **/target/ **/dist/ **/node_modules/ +**/.DS_Store /test # Ignore output.css files diff --git a/business/src/pages/history.rs b/business/src/pages/history.rs index e303453..4bfb272 100644 --- a/business/src/pages/history.rs +++ b/business/src/pages/history.rs @@ -1,47 +1,220 @@ use crate::contexts::OrderDataStore; - -use fuente::mass::HistoryIcon; +use fuente::{ + mass::HistoryIcon, + models::{OrderStatus, OrderInvoiceState}, +}; use yew::prelude::*; +#[derive(Clone, PartialEq)] +enum HistoryFilter { + Completed, + Canceled, +} + #[function_component(HistoryPage)] pub fn history_page() -> Html { let order_ctx = use_context::().expect("No order context found"); let orders = order_ctx.order_history(); + let filter_state = use_state(|| HistoryFilter::Completed); + let selected_order = use_state(|| None::); + + let filtered_orders = orders + .iter() + .filter(|order| match *filter_state { + HistoryFilter::Completed => order.get_order_status() == OrderStatus::Completed, + HistoryFilter::Canceled => order.get_order_status() == OrderStatus::Canceled, + }) + .collect::>(); + + if let Some(order_id) = (*selected_order).clone() { + if let Some(order) = orders.iter().find(|o| o.id() == order_id) { + return html! { + + }; + } + } + html! {
-

{"Order History"}

- {if orders.is_empty() { - html! { - - } +
+

{"Order History"}

+
+ + +
+
+ + {if filtered_orders.is_empty() { + html! { } } else { html! { -
- {for orders.iter().map(|order| { +
+ {filtered_orders.iter().map(|order| { let order_req = order.get_order_request(); - let address = order_req.address; let profile = order_req.profile; - + let order_id = order.id(); + let selected = selected_order.clone(); + html! { -
+
-

{order.id()}

-

{profile.nickname()}

-
-
-

- {address.lookup().display_name()} -

+
+

{profile.nickname()}

+

+ {format!("Order #{}", &order.id()[..8])} +

+
+
+

+ {format!("{:.2} SRD", order_req.products.total())} +

+

+ {order.get_order_status().display()} +

+
} - })} + }).collect::()}
} }}
} } + +#[derive(Properties, Clone, PartialEq)] +struct OrderDetailsProps { + order: OrderInvoiceState, + on_back: Callback, +} + +#[function_component(OrderDetails)] +fn order_details(props: &OrderDetailsProps) -> Html { + let order_req = props.order.get_order_request(); + let products = order_req.products.counted_products(); + + html! { +
+
+ +

+ {format!("Order Details #{}", &props.order.id()[..8])} +

+
+ +
+
+
+

{"Customer Information"}

+

{format!("Name: {}", order_req.profile.nickname())}

+

{format!("Phone: {}", order_req.profile.telephone())}

+

{format!("Email: {}", order_req.profile.email())}

+
+ +
+

{"Delivery Address"}

+

{order_req.address.lookup().display_name()}

+
+ +
+

{"Order Status"}

+

+ {props.order.get_order_status().display()} +

+
+
+ +
+

{"Order Items"}

+
+ {products.iter().map(|(product, count)| { + let subtotal = product.price().parse::().unwrap() * *count as f64; + html! { +
+
+

{product.name()}

+

+ {format!("{} x {} SRD", count, product.price())} +

+
+

{format!("{:.2} SRD", subtotal)}

+
+ } + }).collect::()} + +
+

{"Total"}

+

{format!("{:.2} SRD", order_req.products.total())}

+
+
+
+
+
+ } +} #[function_component(BlankHistory)] pub fn history_page() -> Html { html! { diff --git a/consumer/src/consumer.rs b/consumer/src/consumer.rs index 271db3f..286241b 100644 --- a/consumer/src/consumer.rs +++ b/consumer/src/consumer.rs @@ -1,7 +1,7 @@ use consumer::{ contexts::{ CartProvider, CommerceDataProvider, ConsumerDataAction, ConsumerDataProvider, - ConsumerDataStore, LiveOrderProvider, + ConsumerDataStore, LiveOrderProvider, FavoritesProvider, }, pages::NewProfilePage, router::ConsumerPages, @@ -89,7 +89,9 @@ fn app_context(props: &ChildrenProps) -> Html { - {props.children.clone()} + + {props.children.clone()} + diff --git a/consumer/src/contexts/favorites.rs b/consumer/src/contexts/favorites.rs new file mode 100644 index 0000000..d5268b7 --- /dev/null +++ b/consumer/src/contexts/favorites.rs @@ -0,0 +1,126 @@ +use std::rc::Rc; +use fuente::models::FavoriteStore; +use nostr_minions::{browser_api::IdbStoreManager, key_manager::NostrIdStore}; +use yew::{platform::spawn_local, prelude::*}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct FavoritesData { + has_loaded: bool, + favorites: Vec, +} + +impl FavoritesData { + pub fn is_loaded(&self) -> bool { + self.has_loaded + } + + pub fn get_favorites(&self) -> Vec { + self.favorites.clone() + } + + pub fn is_favorite(&self, commerce_id: &str) -> bool { + self.favorites.iter().any(|f| f.commerce_id == commerce_id) + } +} + +pub enum FavoritesAction { + LoadFavorites(Vec), + AddFavorite(FavoriteStore), + RemoveFavorite(String), + SetLoaded, +} + +impl Reducible for FavoritesData { + type Action = FavoritesAction; + + fn reduce(self: Rc, action: Self::Action) -> Rc { + match action { + FavoritesAction::LoadFavorites(favorites) => Rc::new(FavoritesData { + has_loaded: self.has_loaded, + favorites, + }), + FavoritesAction::AddFavorite(favorite) => { + let db_entry = favorite.clone(); + spawn_local(async move { + if let Err(e) = db_entry.save_to_store().await { + gloo::console::error!("Failed to save favorite:", e); + } + }); + + let mut favorites = self.favorites.clone(); + favorites.push(favorite); + + Rc::new(FavoritesData { + has_loaded: self.has_loaded, + favorites, + }) + } + FavoritesAction::RemoveFavorite(commerce_id) => { + let mut favorites = self.favorites.clone(); + if let Some(favorite) = favorites.iter().find(|f| f.commerce_id == commerce_id).cloned() { + spawn_local(async move { + if let Err(e) = favorite.delete_from_store().await { + gloo::console::error!("Failed to delete favorite:", e); + } + }); + } + + favorites.retain(|f| f.commerce_id != commerce_id); + + Rc::new(FavoritesData { + has_loaded: self.has_loaded, + favorites, + }) + } + FavoritesAction::SetLoaded => Rc::new(FavoritesData { + has_loaded: true, + favorites: self.favorites.clone(), + }), + } + } +} + +pub type FavoritesStore = UseReducerHandle; + +#[derive(Clone, Debug, Properties, PartialEq)] +pub struct FavoritesChildren { + pub children: Children, +} + +#[function_component(FavoritesProvider)] +pub fn favorites_provider(props: &FavoritesChildren) -> Html { + let ctx = use_reducer(|| FavoritesData { + has_loaded: false, + favorites: vec![], + }); + + let ctx_clone = ctx.clone(); + let key_ctx = use_context::().expect("NostrIdStore not found"); + + use_effect_with(key_ctx, move |key_ctx| { + if let Some(keys) = key_ctx.get_nostr_key() { + let ctx = ctx_clone.clone(); + spawn_local(async move { + match FavoriteStore::retrieve_all_from_store().await { + Ok(favorites) => { + // Filter favorites for current user + let user_favorites = favorites + .into_iter() + .filter(|f| f.user_id == keys.public_key()) + .collect(); + ctx.dispatch(FavoritesAction::LoadFavorites(user_favorites)); + } + Err(e) => gloo::console::error!("Failed to load favorites:", e), + } + ctx.dispatch(FavoritesAction::SetLoaded); + }); + } + || {} + }); + + html! { + context={ctx}> + {props.children.clone()} + > + } +} \ No newline at end of file diff --git a/consumer/src/contexts/mod.rs b/consumer/src/contexts/mod.rs index ea42139..becc5b0 100644 --- a/consumer/src/contexts/mod.rs +++ b/consumer/src/contexts/mod.rs @@ -2,7 +2,9 @@ mod cart; mod commerce_data; mod consumer_data; mod live_order; +mod favorites; pub use cart::*; pub use commerce_data::*; pub use consumer_data::*; pub use live_order::*; +pub use favorites::*; diff --git a/consumer/src/pages/favorites.rs b/consumer/src/pages/favorites.rs index f9b0024..3e60b73 100644 --- a/consumer/src/pages/favorites.rs +++ b/consumer/src/pages/favorites.rs @@ -1,22 +1,131 @@ use super::PageHeader; -use fuente::mass::{HeartIcon, SimpleFormButton}; +use crate::contexts::{FavoritesStore, CommerceDataStore}; +use crate::router::ConsumerRoute; +use fuente::mass::{HeartIcon, SimpleFormButton, CommerceProfileCard, AppLink}; use yew::prelude::*; +use nostr_minions::key_manager::NostrIdStore; +use crate::contexts::FavoritesAction; +use fuente::models::FavoriteStore; #[function_component(FavoritesPage)] pub fn favorites_page() -> Html { - html! { -
- -
- -

{"No favorites yet"}

-

- {"Hit the button below to create a new order!"} -

+ let favorites_ctx = use_context::().expect("Favorites context not found"); + let commerce_ctx = use_context::().expect("Commerce context not found"); + + // Get all favorites + let favorites = favorites_ctx.get_favorites(); + + // Get all businesses + let commerces = commerce_ctx.commerces(); + + // Filter businesses that are in favorites + let favorite_businesses = commerces.iter() + .filter(|commerce| { + favorites.iter().any(|f| f.commerce_id == commerce.id()) + }) + .collect::>(); + + gloo::console::log!("Found favorite businesses:", favorite_businesses.len()); + + if favorite_businesses.is_empty() { + html! { +
+ +
+ +

{"No favorites yet"}

+

+ {"Add your favorite stores to quickly find them here!"} +

+
+ + route={ConsumerRoute::Home} + class="mb-8" + selected_class="" + > + + {"Browse Stores"} + + > +
+ } + } else { + html! { +
+ +
+ {favorite_businesses.iter().map(|commerce| { + let commerce_data = commerce.profile().clone(); + let commerce_data_clone = commerce_data.clone(); + html! { + + route={ConsumerRoute::Commerce { + commerce_id: commerce.id().to_string() + }} + class="w-full" + selected_class="" + > +
+ + +
+
> + } + }).collect::()} +
- - {"Create an Order"} - -
+ } } } + +// Add the FavoriteButton component here as well since we're using it +#[derive(Properties, Clone, PartialEq)] +pub struct FavoriteButtonProps { + pub commerce_id: String, + pub commerce_data: fuente::models::CommerceProfile, +} + +#[function_component(FavoriteButton)] +fn favorite_button(props: &FavoriteButtonProps) -> Html { + let favorites_ctx = use_context::().expect("Favorites context not found"); + let key_ctx = use_context::().expect("NostrIdStore not found"); + + let is_favorite = favorites_ctx.is_favorite(&props.commerce_id); + + let onclick = { + let commerce_id = props.commerce_id.clone(); + let favorites = favorites_ctx.clone(); + let user_id = key_ctx.get_nostr_key().unwrap().public_key(); + + Callback::from(move |e: MouseEvent| { + e.stop_propagation(); + if favorites.is_favorite(&commerce_id) { + favorites.dispatch(FavoritesAction::RemoveFavorite(commerce_id.clone())); + } else { + let favorite = FavoriteStore::new(commerce_id.clone(), user_id.clone()); + favorites.dispatch(FavoritesAction::AddFavorite(favorite)); + } + }) + }; + + html! { + + } +} \ No newline at end of file diff --git a/consumer/src/pages/history.rs b/consumer/src/pages/history.rs index a58bf22..38dd330 100644 --- a/consumer/src/pages/history.rs +++ b/consumer/src/pages/history.rs @@ -1,22 +1,257 @@ use super::PageHeader; -use fuente::mass::{HistoryIcon, SimpleFormButton}; +use fuente::{ + mass::{HistoryIcon, SimpleFormButton}, + models::{OrderInvoiceState, OrderStatus, OrderStateIdb}, +}; use yew::prelude::*; +use nostr_minions::key_manager::NostrIdStore; + +#[derive(Clone, PartialEq)] +enum HistoryFilter { + Completed, + Canceled, +} #[function_component(HistoryPage)] pub fn history_page() -> Html { + let key_ctx = use_context::().expect("NostrIdStore not found"); + let orders_state = use_state(|| Vec::::new()); + let filter_state = use_state(|| HistoryFilter::Completed); + let selected_order = use_state(|| None::); + + // (Loading orders from IndexedDB) + let orders = orders_state.clone(); + use_effect_with((), move |_| { + let orders = orders.clone(); + if let Some(keys) = key_ctx.get_nostr_key() { + yew::platform::spawn_local(async move { + match OrderStateIdb::find_history(&keys).await { + Ok(found_orders) => { + orders.set(found_orders); + } + Err(e) => { + gloo::console::error!("Failed to load orders:", e); + } + } + }); + } + || {} + }); + + let filtered_orders = (*orders_state) + .iter() + .filter(|order| match *filter_state { + HistoryFilter::Completed => order.get_order_status() == OrderStatus::Completed, + HistoryFilter::Canceled => order.get_order_status() == OrderStatus::Canceled, + }) + .collect::>(); + + if let Some(order_id) = (*selected_order).clone() { + if let Some(order) = (*orders_state) + .iter() + .find(|o| o.id() == order_id) + { + return html! { + + }; + } + } + + if !filtered_orders.is_empty() { + html! { +
+
+

{"Order History"}

+
+ + +
+
+ +
+ {for filtered_orders.iter().map(|order| { + let order_req = order.get_order_request(); + let order_id = order.id(); + let selected = selected_order.clone(); + let profile = order_req.profile; + + html! { +
+
+
+

{profile.nickname()}

+
+
+

+ {format!("{:.2} SRD", order_req.products.total())} +

+
+
+
+

+ {format!("Order #{}", &order.id()[..8])} +

+

+ {order.get_order_status().display()} +

+
+
+ } + })} +
+
+ } + } else { + html! { +
+ +
+ +

{"No history yet"}

+

+ {"Hit the button below to create a new order!"} +

+
+ + {"Create an Order"} + +
+ } + } +} + +#[derive(Properties, Clone, PartialEq)] +struct OrderDetailsProps { + order: OrderInvoiceState, + on_back: Callback, +} + +#[function_component(OrderDetails)] +fn order_details(props: &OrderDetailsProps) -> Html { + let order_req = props.order.get_order_request(); + let products = order_req.products.counted_products(); + let profile = order_req.profile; + html! { -
- -
- -

{"No history yet"}

-

- {"Hit the button below to create a new order!"} -

+
+
+ +

+ {format!("Order Details #{}", &props.order.id()[..8])} +

+
+ +
+
+
+

{"Customer Information"}

+
+

{"Name: "}{profile.nickname()}

+

{"Phone: "}{profile.telephone()}

+

{"Email: "}{profile.email()}

+
+
+ +
+

{"Delivery Address"}

+

{order_req.address.lookup().display_name()}

+
+ +
+

{"Order Status"}

+

+ {props.order.get_order_status().display()} +

+
+
+ +
+

{"Order Items"}

+
+ {products.iter().map(|(product, count)| { + let subtotal = product.price().parse::().unwrap() * *count as f64; + html! { +
+
+

{product.name()}

+

+ {format!("{} x {} SRD", count, product.price())} +

+
+

{format!("{:.2} SRD", subtotal)}

+
+ } + }).collect::()} + +
+

{"Total"}

+

{format!("{:.2} SRD", order_req.products.total())}

+
+
+
- - {"Create an Order"} -
} } diff --git a/consumer/src/pages/home.rs b/consumer/src/pages/home.rs index e2d6136..25794da 100644 --- a/consumer/src/pages/home.rs +++ b/consumer/src/pages/home.rs @@ -1,9 +1,12 @@ -use crate::{contexts::CommerceDataStore, router::ConsumerRoute}; +use crate::{contexts::CommerceDataStore, router::ConsumerRoute, contexts::FavoritesStore}; use fuente::mass::{ AppLink, CommerceProfileCard, HeartIcon, HistoryIcon, HomeIcon, LoadingScreen, LookupIcon, MenuBarsIcon, ShoppingCartIcon, UserBadgeIcon, }; use yew::prelude::*; +use fuente::models::FavoriteStore; +use nostr_minions::key_manager::NostrIdStore; +use crate::contexts::FavoritesAction; #[function_component(HomePage)] pub fn home_page() -> Html { @@ -37,13 +40,20 @@ pub fn home_page() -> Html {
{businesses.iter().map(|profile| { + let commerce_data = profile.profile().clone(); html! { - - class="w-64" - selected_class="" - route={ConsumerRoute::Commerce { commerce_id: profile.id().to_string() }}> - - > + + class="w-64" + selected_class="" + route={ConsumerRoute::Commerce { commerce_id: profile.id().to_string() }}> +
+ + +
+
> } }).collect::()}
@@ -53,6 +63,54 @@ pub fn home_page() -> Html { } } +#[derive(Properties, Clone, PartialEq)] +pub struct HomeFavoriteButtonProps { + pub commerce_id: String, + pub commerce_data: fuente::models::CommerceProfile, +} + +#[function_component(FavoriteButton)] +fn favorite_button(props: &HomeFavoriteButtonProps) -> Html { + let favorites_ctx = use_context::().expect("Favorites context not found"); + let key_ctx = use_context::().expect("NostrIdStore not found"); + + let is_favorite = favorites_ctx.is_favorite(&props.commerce_id); + + let onclick = { + let commerce_id = props.commerce_id.clone(); + let favorites = favorites_ctx.clone(); + let user_id = key_ctx.get_nostr_key().unwrap().public_key(); + + Callback::from(move |e: MouseEvent| { + e.stop_propagation(); + if favorites.is_favorite(&commerce_id) { + favorites.dispatch(FavoritesAction::RemoveFavorite(commerce_id.clone())); + } else { + let favorite = FavoriteStore::new(commerce_id.clone(), user_id.clone()); + favorites.dispatch(FavoritesAction::AddFavorite(favorite)); + } + }) + }; + + html! { + + } +} + #[derive(Clone, PartialEq)] pub enum CommerceFilter { All, diff --git a/driver/src/contexts/live_order.rs b/driver/src/contexts/live_order.rs index e472e68..6d18b70 100644 --- a/driver/src/contexts/live_order.rs +++ b/driver/src/contexts/live_order.rs @@ -1,11 +1,12 @@ use std::rc::Rc; use fuente::models::{ - OrderInvoiceState, OrderStatus, DRIVER_HUB_PRIV_KEY, DRIVER_HUB_PUB_KEY, NOSTR_KIND_ORDER_STATE, + OrderInvoiceState, OrderStateIdb, OrderStatus, DRIVER_HUB_PRIV_KEY, DRIVER_HUB_PUB_KEY, NOSTR_KIND_ORDER_STATE }; use nostr_minions::{key_manager::NostrIdStore, relay_pool::NostrProps}; use nostro2::{relays::NostrSubscription, keypair::NostrKeypair}; -use yew::prelude::*; +use yew::{platform::spawn_local, prelude::*}; +use nostr_minions::browser_api::IdbStoreManager; #[derive(Clone, Debug, PartialEq, Eq)] pub struct OrderHub { @@ -157,10 +158,20 @@ pub fn commerce_data_sync() -> Html { if let Ok(order_status) = OrderInvoiceState::try_from(decrypted) { if let Some(signed_note) = order_status.get_courier() { if signed_note.pubkey == my_keys.public_key() { + // First save to IndexedDB for any order belonging to this driver + let db_entry = OrderStateIdb::new(note.clone()); + if let Ok(entry) = db_entry { + spawn_local(async move { + if let Err(e) = entry.save_to_store().await { + gloo::console::error!("Failed to save order to IndexedDB:", e); + } else { + gloo::console::log!("Order saved to IndexedDB"); + } + }); + } + match order_status.get_order_status() { OrderStatus::Canceled => { - // TODO - // add to local history gloo::console::info!( "Order Canceled: ", format!("{:?}", order_status.get_order_status()) @@ -170,8 +181,6 @@ pub fn commerce_data_sync() -> Html { )); } OrderStatus::Completed => { - // TODO - // add to local history gloo::console::info!( "Order Completed: ", format!("{:?}", order_status.get_order_status()) @@ -181,7 +190,6 @@ pub fn commerce_data_sync() -> Html { )); } _ => { - // If my key matches assigned courier means im assigned gloo::console::info!( "New LIVE Order: ", format!("{:?}", order_status.get_order_status()) diff --git a/driver/src/pages/history.rs b/driver/src/pages/history.rs new file mode 100644 index 0000000..6a05970 --- /dev/null +++ b/driver/src/pages/history.rs @@ -0,0 +1,278 @@ +use fuente::{ + mass::HistoryIcon, + models::{OrderStatus, OrderInvoiceState}, +}; +use fuente::models::OrderStateIdb; +use nostr_minions::{browser_api::IdbStoreManager, key_manager::NostrIdStore}; +use yew::prelude::*; +use fuente::models::DRIVER_HUB_PRIV_KEY; + +#[derive(Clone, PartialEq)] +enum HistoryFilter { + Completed, + Canceled, +} + +#[function_component(HistoryPage)] +pub fn history_page() -> Html { + let key_ctx = use_context::().expect("NostrIdStore not found"); + let orders_state = use_state(|| Vec::::new()); + let filter_state = use_state(|| HistoryFilter::Completed); + let selected_order = use_state(|| None::); + + let orders = orders_state.clone(); + use_effect_with((), { + let orders = orders.clone(); + move |_| { + if let Some(keys) = key_ctx.get_nostr_key() { + yew::platform::spawn_local(async move { + // Getting all orders from IDB directly + match OrderStateIdb::retrieve_all_from_store().await { + Ok(all_orders) => { + let mut history_orders = Vec::new(); + + for order_entry in all_orders { + let state_note = order_entry.signed_note(); + let hub_keys = nostro2::keypair::NostrKeypair::new(DRIVER_HUB_PRIV_KEY) + .expect("Failed to create hub keys"); + + if let Ok(decrypted) = hub_keys.decrypt_nip_04_content(&state_note) { + if let Ok(order_state) = OrderInvoiceState::try_from(decrypted) { + if let Some(courier) = order_state.get_courier() { + if courier.pubkey == keys.public_key() { + let status = order_state.get_order_status(); + if status == OrderStatus::Completed || status == OrderStatus::Canceled { + gloo::console::log!("Added to history with status:", status.display()); + history_orders.push(order_state); + } + } + } + } + } else { + gloo::console::warn!("Failed to decrypt order:", order_entry.id()); + } + } + orders.set(history_orders); + } + Err(e) => { + gloo::console::error!("Failed to retrieve orders from IDB:", e); + } + } + }); + } + || {} + } + }); + + let filtered_orders = (*orders).iter() + .filter(|order| { + let status = order.get_order_status(); + let matches = match *filter_state { + HistoryFilter::Completed => status == OrderStatus::Completed, + HistoryFilter::Canceled => status == OrderStatus::Canceled, + }; + matches + }) + .collect::>(); + + if let Some(order_id) = (*selected_order).clone() { + if let Some(order) = orders.iter().find(|o| o.id() == order_id) { + return html! { + + }; + } + } + + html! { +
+
+

{"Order History"}

+
+ + +
+
+ + {if filtered_orders.is_empty() { + html! { +
+ +

{"No deliveries yet"}

+

+ {"Your completed deliveries will appear here"} +

+
+ } + } else { + html! { +
+ {filtered_orders.iter().map(|order| { + let order_req = order.get_order_request(); + let profile = order_req.profile; + let order_id = order.id(); + let selected = selected_order.clone(); + + html! { +
+
+
+

{profile.nickname()}

+

+ {format!("Order #{}", &order.id()[..8])} +

+

+ {order_req.address.lookup().display_name()} +

+
+
+

+ {format!("{:.2} SRD", order_req.products.total())} +

+

+ {order.get_order_status().display()} +

+
+
+
+ } + }).collect::>()} +
+ } + }} +
+ } +} + +#[derive(Properties, Clone, PartialEq)] +struct OrderDetailsProps { + order: OrderInvoiceState, + on_back: Callback, +} + +#[function_component(OrderDetails)] +fn order_details(props: &OrderDetailsProps) -> Html { + let order_req = props.order.get_order_request(); + let products = order_req.products.counted_products(); + + html! { +
+
+ +

+ {format!("Delivery Details #{}", &props.order.id()[..8])} +

+
+ +
+
+
+

{"Customer Information"}

+
+

{format!("Name: {}", order_req.profile.nickname())}

+

{format!("Phone: {}", order_req.profile.telephone())}

+
+
+ +
+

{"Delivery Address"}

+

{order_req.address.lookup().display_name()}

+
+ +
+

{"Delivery Status"}

+

+ {props.order.get_order_status().display()} +

+
+
+ +
+

{"Order Items"}

+
+ {products.iter().map(|(product, count)| { + let subtotal = product.price().parse::().unwrap() * *count as f64; + html! { +
+
+

{product.name()}

+

+ {format!("{} x {} SRD", count, product.price())} +

+
+

{format!("{:.2} SRD", subtotal)}

+
+ } + }).collect::()} + +
+

{"Total"}

+

{format!("{:.2} SRD", order_req.products.total())}

+
+
+
+
+
+ } +} \ No newline at end of file diff --git a/driver/src/pages/mod.rs b/driver/src/pages/mod.rs index 0f55352..a1e3408 100644 --- a/driver/src/pages/mod.rs +++ b/driver/src/pages/mod.rs @@ -1,4 +1,6 @@ mod driver_profile; mod home; +mod history; pub use driver_profile::*; pub use home::*; +pub use history::*; diff --git a/driver/src/router.rs b/driver/src/router.rs index f2812fa..c19dd88 100644 --- a/driver/src/router.rs +++ b/driver/src/router.rs @@ -2,7 +2,7 @@ use fuente::mass::{AppLink, HistoryIcon, HomeIcon, MenuBarsIcon, UserBadgeIcon}; use yew::prelude::*; use yew_router::prelude::*; -use crate::pages::{HomePage, NewProfileForm}; +use crate::pages::{HomePage, NewProfileForm, HistoryPage}; #[derive(Clone, Routable, PartialEq)] pub enum DriverRoute { @@ -24,7 +24,7 @@ pub fn consumer_pages() -> Html { render = { move |switch: DriverRoute| { match switch { DriverRoute::Home => html!{}, - DriverRoute::History => html!{<>}, + DriverRoute::History => html!{}, DriverRoute::Profile => html!{<> }, diff --git a/fuente/README.md b/fuente/README.md new file mode 100644 index 0000000..2997794 --- /dev/null +++ b/fuente/README.md @@ -0,0 +1,261 @@ +# Data Model Overview + +## Profiles Overview +This section outlines several types of profiles used for securely storing and transmitting personal and business information. Each profile includes essential fields, supports signing, and enables encryption for secure sharing. + +### `ConsumerProfile` + +Represents a consumer's personal profile, including basic information (nickname, phone, email), and facilitates secure sharing and transmission via signing and encryption. + +### `ConsumerAddress` + +Stores location-based data (address and coordinates), supporting secure sharing, signing, and encryption to verify and protect address information. + + +### `CommerceProfile` + +Represents a business's profile, including details like business name, contact info, geolocation, and Lightning Network address, while ensuring secure storage and sharing. + +### `DriverProfile` + +Stores basic driver details (nickname, phone number), supporting systems that require driver profiles, such as ride-sharing or delivery services. + + +### `CoordinateStrings` + +Provides a simple and flexible way to work with coordinates (latitude and longitude), particularly in situations where coordinates need to be serialized or deserialized in string format. + +### Common Key Functionalities +*All profiles share the following key functionalities:* + +**1. Profile Creation:** Each profile is created using a new() method, which initializes the profile with the required fields (e.g., name, contact info, location, etc.). + +**2. Serialization/Deserialization:** Profiles can be converted between different formats (e.g., Rust, JSON, JavaScript) using various traits: + +- `ToString` for converting to a string (JSON). + +- `From` and `Into` for integration with JavaScript-based systems. + +- `TryFrom` for deserialization from JSON. + +**3. Signing Data:** The `signed_data()` method signs the profile using the user's private key, ensuring its authenticity and protection against tampering. + +**4. Giftwrapping:** The `giftwrapped_data()` method encrypts the profile, adding an extra layer of security for secure sharing with a recipient. + +**5. Getter Methods:** Each profile provides getter methods (e.g., `nickname()`, `telephone()`, `email()`, `geolocation()`) to access the profile's information in a "read-only" manner. + +**6. Defaults:** The `Default()` method provides preset values for profiles when specific data is not provided (e.g., default address or coordinates). + +**7. Conversion from Signed Notes:** Some profiles can be converted from a SignedNote using the `TryFrom` method, enabling secure transmission and processing. + +>**General Takeaways** +>- *Security: All profiles support cryptographic features like signing and encryption to ensure authenticity and privacy.* +>- *Flexibility: They support multiple formats (e.g., JSON, JavaScript) for seamless integration across systems.* +>- *Trust: The profiles are designed for environments requiring data verification and secure data exchange, ensuring reliability and privacy.* + +## Orders Overview + +This section covers various structs used to manage, track, and process orders i. Each struct offers unique features for managing the different stages of the order lifecycle, from request to fulfillment, payment, and invoicing. + +### `OrderRequest` + +Represents a consumer’s order request, encapsulating details such as the commerce platform, consumer’s profile, shipping address, and products ordered. It enables the creation, signing, and secure transmission of the order data. + +### `OrderStatus` + +Tracks and manages the various stages of an order’s lifecycle, such as `Pending`, `Preparing`, `Ready for Delivery`, `In Delivery`, `Completed`, and `Canceled`. It ensures consistent communication of the order’s current state. + +### `OrderPaymentStatus` + +Represents the payment status of an order, tracking stages like `Payment Pending`, `Payment Received`, `Payment Failed`, and `Payment Success`. + +### `OrderInvoiceState` + +Handles the state of an order’s invoice, including payment, order status, and courier information. It supports updates and secure transmissions throughout the order lifecycle. + +### `ProductOrder` + +Represents an order containing multiple `ProductItems`, supporting operations like adding/removing items, counting duplicates, and calculating total prices. + + +### `DriverStateUpdate` + +Designed to handle and track updates related to a driver's state, including their profile and real-time geolocation information. +*It includes methods for signing, encrypting, and decrypting updates.* + + +### Common Key Functionalities +*All order-related structs share these common functionalities:* + +**1. Serialization & Deserialization:** +- `ToString` converts the struct into a JSON string, allowing easy transmission and storage. + +- `TryFrom` enables deserialization from JSON strings for easy integration with external systems or databases. + +**2. Default Initialization:** Provides default values for the struct’s fields, enabling quick instantiation without requiring all fields to be specified immediately. + +**3. Signatures for Authentication & Integrity:** +- `sign_request()` (or equivalent methods) signs the order to verify its authenticity and protect the integrity of the data. + +- `giftwrapped_request()` (or equivalent methods) encrypts the signed data for secure transmission to the recipient. + +**4. Updating Statuses:** Methods like `update_status()` or `update_payment_status()` allow for seamless updates to the order’s state, including payment, fulfillment, or courier details. + +**5. Human-Readable Display:** `display()` provides a user-friendly string for the current state or status, making it easy to present to end-users in interfaces or reports. + +>### General Takeaways +>- *Security: All structs support cryptographic features like signing and encryption to ensure authenticity and protect sensitive data.-* +>- *Flexibility: They are compatible with various systems through serialization and deserialization, making them easy to integrate with web APIs, databases, and external services.* +>- *Tracking: Each struct is designed to track and communicate important information related to orders, including statuses, payments, and product details, in a clear and structured manner.* +>- *Compatibility: The use of JSON for serialization ensures compatibility across platforms, whether for communication or data storage.* + +## Products Overview + +This section outlines the main structures that represent different components of an order or product catalog in a system. Each structure serves a unique purpose, but they share common functionalities for managing and processing product data efficiently. + + +### `ProductSide` + +Represents a side of a product in an order (e.g., an optional variant or addition like a side of fries with a meal). It includes the product's identity, position in an order, name, and price. + +### `ProductItem` + +Represents an item in an order or catalog. It includes essential details such as the product's name, price, description, and category. It can also support optional sides (e.g., toppings or variants). + +### `ProductCategory` + +Represents a category of products within the system (e.g., a "Beverages" category in an online store). It organizes products into groups and supports adding or removing items. + +### `ProductMenu` + +Represents a collection of product categories. It manages the organization of categories and their associated products, helping structure an entire catalog or menu. + +### Common Key Functionalities +*These structures share several core functionalities, allowing for seamless handling of product data:* + +**1. Serialization and Deserialization:** +- Serialize to String: Converts the structure into a JSON string. This is useful for storage, network transmission, or logging. It makes the product information easy to transfer or save in databases. +- Deserialize from String: Converts a JSON string back into the structure, allowing data to be retrieved and used by the system (e.g., from an API, database, or file). + +**2. Creating and Managing Objects:** +- Create New Product/Category/Menu: Initialize new instances of products, categories, or menus. *For example, a `ProductItem` can be created with a name, price, and category, while a `ProductCategory` groups products under a specific label like "Beverages."* +- Add and Update: Add new products or categories, or update existing ones. For example, you can add variants (sides) to a product or update a product's name or price in a category or menu. + +**3. Product Information Retrieval:** + +- Retrieve and Sort: Retrieve products or categories in a specific order. Products are listed in the correct sequence, and categories are organized based on an order attribute, ensuring consistency in how data is accessed or displayed. +- Get Product Attributes: Access product details like name, price, description, or category, making it easy to interact with and manipulate the product data. + +**4. Extendable for Additional Attributes:** These structures can be extended to handle more complex product attributes, such as multiple price tiers, inventory counts, or images. This flexibility ensures they can scale with business needs. + +>### Functionality Takeaways +>- *Data Storage and Transmission: The shared serialization and deserialization capabilities allow for easy saving, retrieving, and sharing of product data in systems like databases or APIs.* +>- *Variant and Customization Support: These structures are ideal for products with optional variations or add-ons (e.g., product sizes, toppings), making it easy to manage product customization.* +>- *Efficient Product Management: The ability to organize products into categories or menus makes it simple to update, add, or remove items in bulk, supporting large inventories or dynamic product offerings.* +>- *Flexible Integration: The structures are designed to integrate with external systems and support decentralized systems, allowing for diverse use cases in modern e-commerce environments.* + +## Data Bases / IndexedDB (IDB) Structures Overview + +This section provides an overview of various structures that interact with `IndexedDB` for securely storing and managing consumer, driver, commerce, and order-related data in the browser. These structures ensure data integrity and security while facilitating smooth interactions between web applications and local storage. + + +### `ConsumerAddressIdb` + +Stores and manages consumer address data in `IndexedDB`, including marking an address as the default. + +### `ConsumerProfileIdb` + +Represents and securely stores a consumer's profile, including their public key and signed note, in `IndexedDB`. + +### `CommerceProfileIdb` + +Stores a commercial profile along with its signed note and public key in `IndexedDB`. + +### `DriverProfileIdb` + +Manages and stores a driver’s profile data (including public key and signed note) in IndexedDB for offline access. + +### `ProductMenuIdb` + +Stores a product menu along with a signed note and public key, ensuring integrity and secure storage in IndexedDB. + +### `OrderStateIdb` + +Tracks and stores the state of an order in IndexedDB, including its order ID and associated metadata. + +### Common Key Functionalities +*The following functionalities are common across most of the IDB structures, allowing for efficient management of data in the browser’s local storage (`IndexedDB`):* + +**1. Serialization & Deserialization:** + +- Serialize to JavaScript: The structures can be converted into JavaScript values (`JsValue`) for use in web applications. This makes them compatible with frontend frameworks and easy to store/retrieve data from IndexedDB. +- Deserialize from JavaScript: Converts JavaScript values back into the respective Rust structures, enabling interactions with the browser storage system. +- Serialization/Deserialization of Signed Notes: The structures support serialization of signed notes (which verify the authenticity of the data) to/from their respective formats (e.g., SignedNote). + +**2. Creation & Storage:** + +- Create New Instances: The `new()` method is used to create new instances of each structure (e.g., `ConsumerAddressIdb`, `CommerceProfileIdb`, etc.), initializing them with the necessary data, such as profiles, addresses, or orders. +- Save to `IndexedDB`: Each structure provides methods for saving data to the IndexedDB, ensuring data persists across sessions. +- Retrieve from `IndexedDB`: The structures allow for retrieving data stored in the `IndexedDB`, either through direct queries or by accessing unique identifiers. + +**3. `IndexedDB` Integration:** + +- Configuration Management: Methods like `config()` set the configuration for storing objects in `IndexedDB`, including defining the store and database names. +- Database Upgrades: Structures implement `upgrade_db()` to manage schema upgrades in the `IndexedDB`, ensuring smooth handling of data changes over time. + +**4. Key Management:** + +- Unique Identifiers: Each structure provides a unique key (`nostr_id or pubkey`) that can be used to access, retrieve, or delete individual records in `IndexedDB`. +Default/Primary Data: Many structures support marking specific records as the "default" (e.g., an address or profile), allowing users to easily manage their preferences. + + +>### Functionality Takeaways +>- *Persistent Storage: All structures store data securely and persistently in the browser’s IndexedDB, allowing for offline access and ensuring data is retained across sessions.* +>- *Secure Data Handling: Signed notes and public keys are used to ensure data integrity and secure storage of sensitive information (e.g., profiles, addresses, orders).* +>- *Efficient Data Access: These structures offer easy methods to retrieve, update, and delete data, with unique identifiers ensuring precise access to specific records.* +>- *Seamless Integration with Web Applications: The structures support smooth conversion between Rust and JavaScript, enabling them to work seamlessly with modern web-based applications.* +>- *Simplified User Preferences: Features like default address or profile management make it easy for users to manage their preferences in client-side applications.* + +## Admin Overview + +This section outlines the core components for managing the administrative configuration of an application by enabling secure and efficient management of access control lists (whitelists, blacklists), user registrations, and exchange rates. They ensure that only authorized entities can interact with specific services and allow administrators to manage, update, and validate critical system configurations with cryptographic security. + +### `AdminConfiguration` + +Manages the configuration settings for an admin system within the application. + +*Responsible for controlling access through whitelists (admin, commerce, couriers), blacklists (consumer), user registrations, and exchange rates.* + +### `AdminConfigurationType` + +Categorizes the different configuration types (e.g., admin whitelist, consumer blacklist) an admin can modify. + +### `AdminServerRequest` + +Handles configuration-related requests from administrators. It securely manages the configuration data (e.g., whitelist updates, exchange rate changes), ensuring that requests are signed and validated for authenticity before being processed. + +### `LastSyncTime` + +Represents a timestamp that stores the last synchronization time for a particular operation or event. + +### Common Key Functionalities + +**1. Default Configuration:** Default values are set when new instances are created (e.g., empty lists for whitelists and blacklists, exchange rate set to 1.0). + +**2. Signing and Validation:** Each configuration (e.g., whitelists, blacklists) is signed using the admin’s private key, ensuring authenticity. The system can validate the integrity of these configurations using encryption and cryptographic tags. + +**3. Configuration Management:** Admins can modify lists of allowed entities (whitelists) or blocked entities (blacklists). Configuration types can be changed or updated. + +**4.Data Conversion:** Admin configurations and types can be converted between strings, numeric values, or serialized formats for easy communication or storage. + +**5. Secure Requests:** Configuration changes are submitted via encrypted, signed requests, ensuring only authorized admins can modify the settings. + +**6. Exchange Rate Management:** Admins can set or retrieve exchange rates, which are essential for various application functionalities (e.g., currency conversion). + + +>### Functionality Takeaways +>- *Centralized Access Control: AdminConfiguration enables the central management of whitelists, blacklists, and other critical configuration data (e.g., exchange rates).* +>- *Secure Configuration Handling: All configurations are signed, validated, and securely managed, ensuring that only trusted entities can access or modify critical data.* +>- *Efficient Data Conversion: Supports seamless conversion of configuration types and request data, improving flexibility and type safety.* +>- *Administrator Control: Admins have the power to manage, modify, and validate critical settings, ensuring the right users and entities can access or interact with specific services.* + diff --git a/fuente/src/models/favorites.rs b/fuente/src/models/favorites.rs new file mode 100644 index 0000000..e2e7e21 --- /dev/null +++ b/fuente/src/models/favorites.rs @@ -0,0 +1,53 @@ +use ::serde::{Deserialize, Serialize}; +use wasm_bindgen::JsValue; +use nostr_minions::browser_api::IdbStoreManager; +use super::{DB_NAME_FUENTE, DB_VERSION_FUENTE, STORE_NAME_CONSUMER_FAVORITES}; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct FavoriteStore { + pub commerce_id: String, + pub user_id: String, + pub timestamp: u64, +} + +impl FavoriteStore { + pub fn new(commerce_id: String, user_id: String) -> Self { + Self { + commerce_id, + user_id, + timestamp: js_sys::Date::now() as u64, + } + } + + pub fn id(&self) -> String { + format!("{}-{}", self.user_id, self.commerce_id) + } +} + +impl TryFrom for FavoriteStore { + type Error = JsValue; + fn try_from(value: JsValue) -> Result { + Ok(serde_wasm_bindgen::from_value(value)?) + } +} + +impl Into for FavoriteStore { + fn into(self) -> JsValue { + serde_wasm_bindgen::to_value(&self).unwrap() + } +} + +impl IdbStoreManager for FavoriteStore { + fn config() -> nostr_minions::browser_api::IdbStoreConfig { + nostr_minions::browser_api::IdbStoreConfig { + db_name: DB_NAME_FUENTE, + db_version: DB_VERSION_FUENTE, + store_name: STORE_NAME_CONSUMER_FAVORITES, + document_key: "commerce_id", + } + } + + fn key(&self) -> JsValue { + JsValue::from_str(&self.id()) + } +} \ No newline at end of file diff --git a/fuente/src/models/mod.rs b/fuente/src/models/mod.rs index 03befa8..da7912c 100644 --- a/fuente/src/models/mod.rs +++ b/fuente/src/models/mod.rs @@ -11,6 +11,7 @@ mod gps; mod nostr_kinds; mod orders; mod products; +mod favorites; pub use address::*; pub use admin_configs::*; pub use commerce::*; @@ -20,6 +21,7 @@ pub use gps::*; pub use nostr_kinds::*; pub use orders::*; pub use products::*; +pub use favorites::*; // pub mod sync; // // TODO @@ -49,6 +51,7 @@ pub const STORE_NAME_CONSUMER_PROFILES: &str = "user_profiles"; pub const STORE_NAME_CONSUMER_ADDRESSES: &str = "consumer_address"; pub const STORE_NAME_PRODUCT_LISTS: &str = "product_lists"; pub const STORE_NAME_ORDER_HISTORY: &str = "order_history"; +pub const STORE_NAME_CONSUMER_FAVORITES: &str = "consumer_favorites"; pub const ADMIN_WHITELIST: [&str; 2] = [ "decfef1c4a027fe815eda8ea5748aa0d3e971c4c377423a49d94fa0fc3e25575", @@ -101,6 +104,10 @@ fn upgrade_fuente_db(db: web_sys::IdbDatabase) -> Result<(), JsValue> { orders::OrderStateIdb::create_data_store(&db)?; gloo::console::log!("Order history store created"); } + if !db.object_store_names().contains(STORE_NAME_CONSUMER_FAVORITES) { + FavoriteStore::create_data_store(&db)?; + gloo::console::log!("Favorites store created"); + } gloo::console::log!("Fuente database upgraded"); Ok(()) } diff --git a/public/styles/input.css b/public/styles/input.css index e5f5e12..90b8230 100644 --- a/public/styles/input.css +++ b/public/styles/input.css @@ -2,13 +2,12 @@ @tailwind components; @tailwind utilities; -@layer base { - :root { - --color-primary: 59 17 151; - } - body { - @apply font-product; - } +.container{ + width: 95%; +} + +.max-w-6xl{ + width: 95%; } @font-face { diff --git a/tailwind.config.cjs b/tailwind.config.cjs index 7b55e80..184bb04 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -11,12 +11,17 @@ module.exports = { 'fuente-light': '#2aa1e2', 'fuente': '#4167e8', 'fuente-dark': '#3b1197', - + 'fuente-forms': "#3859c7", + "fuente-buttons": "#6fe5fa", + "fuente-orange": "#f4801b" }, fontFamily: { 'product': ['OpenSans', 'sans-serif'], 'mplus': ['MPlus', 'sans-serif'], }, + backgroundImage: { + logo: `url('/templates/img/Logo Fuente.jpeg')` + } }, }, }; diff --git a/templates/components/all-categories.html b/templates/components/all-categories.html new file mode 100644 index 0000000..c42ef37 --- /dev/null +++ b/templates/components/all-categories.html @@ -0,0 +1,6 @@ +
+
+ Book Image +
+

{"Books"}

+
\ No newline at end of file diff --git a/templates/components/cart-details.html b/templates/components/cart-details.html new file mode 100644 index 0000000..9b2c6c3 --- /dev/null +++ b/templates/components/cart-details.html @@ -0,0 +1,27 @@ + + + Product Image + + +

{"Brand of shoes"}

+

{"Woman Rainbow shoes"}

+

{"CODE: 001212"}

+ + +
+ + {"1"} + +
+ + +

{"$99.99"}

+ + + + + \ No newline at end of file diff --git a/templates/components/categories-offers.html b/templates/components/categories-offers.html new file mode 100644 index 0000000..efd9851 --- /dev/null +++ b/templates/components/categories-offers.html @@ -0,0 +1,20 @@ +
+
+
+ Sneaker Product +

+ {"50% Off Only today!"} + {"In evany store"} +

+
+
+
+
+ iPhone Product +

+ {"Shop today Receive today!"} + {"In mytechy store"} +

+
+
+
\ No newline at end of file diff --git a/templates/components/favorites-product.html b/templates/components/favorites-product.html new file mode 100644 index 0000000..9d9a109 --- /dev/null +++ b/templates/components/favorites-product.html @@ -0,0 +1,26 @@ +
+
+ Favorites Image + + + +
+ +

{"T-shirt Jurassic Vibes"}

+

{"DEMO STORE"}

+
+ + + +

{"5.0 (3 reviews)"}

+
+ +
+

{"$14.99"}

+ +
+
\ No newline at end of file diff --git a/templates/components/footer.html b/templates/components/footer.html new file mode 100644 index 0000000..07168be --- /dev/null +++ b/templates/components/footer.html @@ -0,0 +1,35 @@ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
\ No newline at end of file diff --git a/templates/components/header.html b/templates/components/header.html new file mode 100644 index 0000000..01877c9 --- /dev/null +++ b/templates/components/header.html @@ -0,0 +1,41 @@ +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/templates/components/hot-categories-category.html b/templates/components/hot-categories-category.html new file mode 100644 index 0000000..d7acb12 --- /dev/null +++ b/templates/components/hot-categories-category.html @@ -0,0 +1,3 @@ +
+

{"Books"}

+
\ No newline at end of file diff --git a/templates/components/hot-categories-image.html b/templates/components/hot-categories-image.html new file mode 100644 index 0000000..8501e6b --- /dev/null +++ b/templates/components/hot-categories-image.html @@ -0,0 +1 @@ +iPhone Product \ No newline at end of file diff --git a/templates/components/navbar.html b/templates/components/navbar.html new file mode 100644 index 0000000..2ccc501 --- /dev/null +++ b/templates/components/navbar.html @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/templates/components/profile.html b/templates/components/profile.html new file mode 100644 index 0000000..734f5f4 --- /dev/null +++ b/templates/components/profile.html @@ -0,0 +1,16 @@ +
+
+

{"Address Registered"}

+ {"Cali Street, New York, United States, 11232"} +
+ + +
\ No newline at end of file diff --git a/templates/components/steps.html b/templates/components/steps.html new file mode 100644 index 0000000..c1e09da --- /dev/null +++ b/templates/components/steps.html @@ -0,0 +1,11 @@ +
+
+

{"1"}

+

{"Payment"}

+
+ +
+

{"2"}

+

{"Confirmation"}

+
+
\ No newline at end of file diff --git a/templates/components/support.html b/templates/components/support.html new file mode 100644 index 0000000..759b2d1 --- /dev/null +++ b/templates/components/support.html @@ -0,0 +1,35 @@ +
+
+
+ + + +
+

{"Support 24/7"}

+

{"Call to our support team"}

+

{"Availble 24 hours 7 days of week!"}

+
+
+
+ + + +
+

{"Track your package!"}

+

{"You can track your package"}

+

{"on real time!"}

+
+
+
+ + + + +
+

{"Shop Secure"}

+

{"Our website compiles"}

+

{"with security standards."}

+
+
+
+
\ No newline at end of file diff --git a/templates/img/Logo Fuente.jpeg b/templates/img/Logo Fuente.jpeg new file mode 100644 index 0000000..66b1343 Binary files /dev/null and b/templates/img/Logo Fuente.jpeg differ diff --git a/templates/img/bitcoin.png b/templates/img/bitcoin.png new file mode 100644 index 0000000..88a1a9b Binary files /dev/null and b/templates/img/bitcoin.png differ diff --git a/templates/img/candy.jpg b/templates/img/candy.jpg new file mode 100644 index 0000000..84015f2 Binary files /dev/null and b/templates/img/candy.jpg differ diff --git a/templates/img/company.png b/templates/img/company.png new file mode 100644 index 0000000..08bfcb9 Binary files /dev/null and b/templates/img/company.png differ diff --git a/templates/img/couch.png b/templates/img/couch.png new file mode 100644 index 0000000..7c3fe1e Binary files /dev/null and b/templates/img/couch.png differ diff --git a/templates/img/hp.jpg b/templates/img/hp.jpg new file mode 100644 index 0000000..8fce9e6 Binary files /dev/null and b/templates/img/hp.jpg differ diff --git a/templates/img/iphone.png b/templates/img/iphone.png new file mode 100644 index 0000000..111c028 Binary files /dev/null and b/templates/img/iphone.png differ diff --git a/templates/img/laptop.png b/templates/img/laptop.png new file mode 100644 index 0000000..1789253 Binary files /dev/null and b/templates/img/laptop.png differ diff --git a/templates/img/mapa.png b/templates/img/mapa.png new file mode 100644 index 0000000..490ed66 Binary files /dev/null and b/templates/img/mapa.png differ diff --git a/templates/img/minions.jpg b/templates/img/minions.jpg new file mode 100644 index 0000000..1d4817b Binary files /dev/null and b/templates/img/minions.jpg differ diff --git a/templates/img/ninja.png b/templates/img/ninja.png new file mode 100644 index 0000000..c55febd Binary files /dev/null and b/templates/img/ninja.png differ diff --git a/templates/img/sneaker_1.png b/templates/img/sneaker_1.png new file mode 100644 index 0000000..8401066 Binary files /dev/null and b/templates/img/sneaker_1.png differ diff --git a/templates/img/sneaker_2.png b/templates/img/sneaker_2.png new file mode 100644 index 0000000..2ce7fc5 Binary files /dev/null and b/templates/img/sneaker_2.png differ diff --git a/templates/img/sneaker_3.png b/templates/img/sneaker_3.png new file mode 100644 index 0000000..8d99c33 Binary files /dev/null and b/templates/img/sneaker_3.png differ diff --git a/templates/img/store.png b/templates/img/store.png new file mode 100644 index 0000000..155f30a Binary files /dev/null and b/templates/img/store.png differ diff --git a/templates/img/ts.jpg b/templates/img/ts.jpg new file mode 100644 index 0000000..f0b7696 Binary files /dev/null and b/templates/img/ts.jpg differ diff --git a/templates/img/tshirt.png b/templates/img/tshirt.png new file mode 100644 index 0000000..cbd9aa1 Binary files /dev/null and b/templates/img/tshirt.png differ diff --git a/templates/img/vr.png b/templates/img/vr.png new file mode 100644 index 0000000..be74c29 Binary files /dev/null and b/templates/img/vr.png differ diff --git a/templates/img/whey.jpg b/templates/img/whey.jpg new file mode 100644 index 0000000..59531ac Binary files /dev/null and b/templates/img/whey.jpg differ diff --git a/templates/img/yumi.jpg b/templates/img/yumi.jpg new file mode 100644 index 0000000..b33ca2a Binary files /dev/null and b/templates/img/yumi.jpg differ diff --git a/templates/json/texts.json b/templates/json/texts.json new file mode 100644 index 0000000..c692f59 --- /dev/null +++ b/templates/json/texts.json @@ -0,0 +1,170 @@ +{ + "nav_books": "Books", + "nav_tech": "Tech", + "nav_clothing": "Clothing", + "nav_hardware": "Hardware", + "nav_pharmacy": "Pharmacy", + "nav_groceries": "Groceries", + "nav_music": "Music", + "nav_movies": "Movies", + "nav_furniture": "Furniture", + + "auth_login_title": "Login", + "auth_login_heading_shop": "Shop", + "auth_login_heading_now": "Now", + "auth_login_form_label": "Write the key", + "auth_login_link_key": "How the key works?", + "auth_login_link_register": "I'm not already registered", + + "auth_register_title": "Register", + "auth_register_heading": "Register", + "auth_register_heading_now": "Now", + "auth_register_form_label_username": "Your username", + "auth_register_form_label_email": "Your email", + "auth_register_form_label_phone": "Phone number", + "auth_register_link_login": "I have an account - Login", + + "profile_address_title": "Profile - Address", + "profile_address_heading": "My Profile", + "profile_address_button_orders": "My Orders", + "profile_address_packages_button": "Track Packages", + "profile_address_new_address_button": "New Address", + "profile_address_personal_information_button": "Personal Information", + "profile_address_address_button": "Address", + "profile_address_address_registered": "Address Registered", + "profile_address_details": "Cali Street, New York, United States, 11232", + "profile_address_edit_button": "Edit", + + "profile_personal_information_title": "Profile - Personal Information", + "profile_personal_information_heading": "My Profile", + "profile_personal_information_orders_button": "My Orders", + "profile_personal_information_packages_button": "Track Packages", + "profile_personal_information_delete_account_button": "Delete my account", + "profile_personal_information_button": "Personal Information", + "profile_personal_information_address_button": "Address", + "profile_personal_information_name": "Christine Amador Russel", + "profile_personal_information_email": "christineam@gmail.com", + "profile_personal_information_phone_number": "+507 7656559012", + "profile_personal_information_edit_button": "Edit", + + "cart_title": "Cart", + "cart_heading": "My Cart", + "cart_text": "Products Details", + "cart_table_heading_details": "Product Details", + "cart_table_heading_quantity": "Quantity", + "cart_table_heading_price": "Price", + "cart_table_product_name": "Brand of shoes", + "cart_table_product_type": "Woman Rainbow shoes", + "cart_table_product_code": "CODE: 001212", + "cart_pre_total": "Pre Total", + "cart_checkout": "Go to checkout", + + "payment_title": "Payment", + "payment_step_1": "Payment", + "payment_step_2": "Confirmation", + "payment_heading": "Your transaction is waiting to be approved by the store", + "payment_detail": "Please do not close the window, approval may take 5 minutes, otherwise your money will be refunded tho the account", + + "confirmation_title": "Confirmation", + "confirmation_step_1": "Payment", + "confirmation_step_2": "Confirmation", + "confirmation_heading": "Yay! Your transaction", + "confirmation_text": "was Processed successfully!", + "confirmation_order": "Order #1234567", + "confirmation_detail": "Track your order on My packages list!", + "confirmation_detail_message": "To receive the driver on time!", + "confirmation_button": "View my packages", + + "error_screen_title": "Error", + "error_screen_step_1": "Payment", + "error_screen_step_2": "Confirmation", + "error_screen_heading": "Sorry, your transaction was denied", + "error_screen_text": "Please try again!", + "error_screen_order": "Order #1234567", + "error_screen_detail": "Please check the status of your transaction", + "error_screen_detail_message": "Check my orders", + + "favorites_products_title": "Favorites - Products", + "favorites_products_heading": "Favorites", + "favorites_products_stores_button": "Stores", + "favorites_products_products_button": "Products", + "favorites_products_detail_heading": "T-shirt Jurassic Vibes", + "favorites_products_detail_store": "DEMO STORE", + "favorites_products_detail_reviews": "5.0 (3 reviews)", + + "favorites_stores_title": "Favorites - Stores", + "favorites_stores_heading": "Favorites", + "favorites_stores_stores_button": "Stores", + "favorites_stores_products_button": "Products", + "favorites_stores_detail_name": "Evany Store", + "favorites_stores_detail_category": "Category: Shoes", + "favorites_stores_detail_review": "5.0 Deliver on time", + + "packages_track_title": "Packages - Track", + "packages_track_heading": "Your Packages", + "packages_track_on_going_button": "On Going", + "packages_track_completed_button": "Completed", + "packages_track_table_heading_order": "Order Number", + "packages_track_table_heading_date": "Purchased Date", + "packages_track_table_heading_details": "Product Details", + "packages_track_table_heading_driver": "Driver Assigned", + "packages_track_product_heading": "Brand of shoes", + "packages_track_product_description": "Woman Rainbow shoes", + "packages_track_product_code": "CODE: 001212", + + "categories_title": "Categories", + "categories_heading": "Hot Categories!", + "categories_books_heading": "Books", + "categories_tech_heading": "Tech", + "categories_movies_heading": "Movies", + "categories_music_heading": "Music", + "category_discount_heading": "50% Off Only today!", + "category_discount_store": "In evany store", + "category_offer_heading": "Shop today Receive today!", + "category_offer_store": "In mytechy store", + "category_categories_heading": "All Categories", + "category_books_heading": "Books", + "category_tech_heading": "Tech", + "category_clothing_heading": "Clothing", + "category_hardware_heading": "Hardware", + "category_pharmacy_heading": "Pharmacy", + "category_groceries_heading": "Groceries", + "category_music_heading": "Music", + "category_movies_heading": "Movies", + + "orders_title": "My orders", + "orders_heading": "My Orders", + "orders_completed_button": "Completed", + "orders_failed_button": "Failed", + "orders_table_heading_order": "Order Number", + "orders_table_heading_date": "Purchased Date", + "orders_table_heading_details": "Product Details", + "orders_table_heading_driver": "Driver Assigned", + "orders_table_heading_status": "Status of Store", + "orders_table_product_brand": "Brand of shoes", + "orders_table_product_description": "Woman Rainbow shoes", + "orders_table_product_code": "CODE: 001212", + "orders_table_status": "Delivered", + + "benefits_support_heading": "Support 24/7", + "benefits_support_text": "Call to our support team", + "benefits_support_text_description": "Availble 24 hours 7 days of week!", + "benefits_track_heading": "Track your package!", + "benefits_track_text": "You can track your package", + "benefits_track_text_description": "on real time!", + "benefits_secure_heading": "Shop Secure", + "benefits_secure_text": "Our website compiles", + "benefits_secure_text_description": "with security standards.", + + "footer_heading": "Fuente", + "footer_about_how_to_buy": "How to buy?", + "footer_about_how_to_sale": "How to sale?", + "footer_about_why_is_secure": "Why is secure?", + "footer_benefits_our_benefits": "Our Benefits", + "footer_benefits_shipping": "Shipping and collections of orders", + "footer_benefits_payment_methods": "Payment Methods", + "footer_politics_terms": "Terms and conditions", + "footer_politics_general": "General policies", + "footer_politics_privacy": "Privacy Policy", + "footer_politics_return": "Return and exchanges" +} \ No newline at end of file diff --git a/templates/views/app/cart.html b/templates/views/app/cart.html new file mode 100644 index 0000000..cfea56f --- /dev/null +++ b/templates/views/app/cart.html @@ -0,0 +1,164 @@ + + + + + + {"Cart"} + + + +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+

{"My Cart"}

+ +
+

{"Products Details"}

+ + + + + + + + + + + + + + + + + + + + +
{"Product Details"}{"Quantity"}{"Price"}
+ Product Image + +

{"Brand of shoes"}

+

{"Woman Rainbow shoes"}

+

{"CODE: 001212"}

+
+
+ + {"1"} + +
+
+

{"$99.99"}

+
+ +
+
+ +
+

+ {"Pre Total"} + {"$299.97"} +

+
+ +
+ +
+
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/app/categories.html b/templates/views/app/categories.html new file mode 100644 index 0000000..5199947 --- /dev/null +++ b/templates/views/app/categories.html @@ -0,0 +1,257 @@ + + + + + + {"Categories"} + + + +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+

{"Hot Categories!"}

+ +
+
+
+

{"Books"}

+
+ iPhone Product + Sneaker Product + Yumi Product +
+

{"Tech"}

+
+ iPhone Product + Sneaker Product + Yumi Product +
+ +
+ iPhone Product +
+

{"Movies"}

+
+ Sneaker Product + Yumi Product + iPhone Product +
+

{"Music"}

+
+ Sneaker Product + Yumi Product +
+
+ +
+
+
+ Sneaker Product +

+ {"50% Off Only today!"} + {"In evany store"} +

+
+
+
+
+ iPhone Product +

+ {"Shop today Receive today!"} + {"In mytechy store"} +

+
+
+
+
+ +
+

{"All Categories"}

+ +
+
+
+ Book Image +
+

{"Books"}

+
+ +
+
+ iPhone Image +
+

{"Tech"}

+
+ +
+
+ Sneaker Image +
+

{"Clothing"}

+
+ +
+
+ Sneaker Image +
+

{"Hardware"}

+
+ +
+
+ Yumi Image +
+

{"Pharmacy"}

+
+ +
+
+ Candy Image +
+

{"Groceries"}

+
+ +
+
+ Taylor Image +
+

{"Music"}

+
+ +
+
+ Minions Image +
+

{"Movies"}

+
+
+
+ +
+
+
+ + + +
+

{"Support 24/7"}

+

{"Call to our support team"}

+

{"Availble 24 hours 7 days of week!"}

+
+
+
+ + + +
+

{"Track your package!"}

+

{"You can track your package"}

+

{"on real time!"}

+
+
+
+ + + + +
+

{"Shop Secure"}

+

{"Our website compiles"}

+

{"with security standards."}

+
+
+
+
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/app/confirmation.html b/templates/views/app/confirmation.html new file mode 100644 index 0000000..99721b9 --- /dev/null +++ b/templates/views/app/confirmation.html @@ -0,0 +1,121 @@ + + + + + + {"Confirmation"} + + + +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
+ +
+
+

{"1"}

+

{"Payment"}

+
+ +
+

{"2"}

+

{"Confirmation"}

+
+
+ +
+
+ +
+ +
+
+

{"Yay! Your transaction"}

+

{"was Processed successfully!"}

+
+ +

{"Order #1234567"}

+ +
+

{"Track your order on My packages list!"}

+

{"To receive the driver on time!"}

+ +
+
+
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/app/error-screen.html b/templates/views/app/error-screen.html new file mode 100644 index 0000000..5c2a96a --- /dev/null +++ b/templates/views/app/error-screen.html @@ -0,0 +1,123 @@ + + + + + + {"Error"} + + + +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
+ +
+
+

{"1"}

+

{"Payment"}

+
+ +
+

{"2"}

+

{"Confirmation"}

+
+
+ +
+
+ + + +
+ +
+
+

{"Sorry, your transaction was denied"}

+

{"Please try again!"}

+
+ +

{"Order #1234567"}

+ +
+

{"Please check the status of your transaction"}

+

{"Before to start shpping again"}

+ +
+
+
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/app/favorites-products.html b/templates/views/app/favorites-products.html new file mode 100644 index 0000000..57dce95 --- /dev/null +++ b/templates/views/app/favorites-products.html @@ -0,0 +1,198 @@ + + + + + + {"Favorites - Products"} + + + +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+

{"Favorites"}

+ +
+ + +
+
+
+ Favorites Image + + + +
+ +

{"T-shirt Jurassic Vibes"}

+

{"DEMO STORE"}

+
+ + + +

{"5.0 (3 reviews)"}

+
+ +
+

{"$14.99"}

+ +
+
+
+
+ Favorites Image + + + +
+ +

{"Harry Potter Book"}

+

{"ANDINA STORE"}

+
+ + + +

{"2.0 (0 reviews)"}

+
+ +
+

{"$49.99"}

+ +
+
+
+
+ Favorites Image + + + +
+ +

{"Whey Protein 100%"}

+

{"WHEY STORE"}

+
+ + + +

{"5.0 (30 reviews)"}

+
+ +
+

{"$99.99"}

+ +
+
+
+
+
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/app/favorites-stores.html b/templates/views/app/favorites-stores.html new file mode 100644 index 0000000..74d09fd --- /dev/null +++ b/templates/views/app/favorites-stores.html @@ -0,0 +1,169 @@ + + + + + + {"Favorites - Stores"} + + + +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+

{"Favorites"}

+ +
+ + +
+
+
+ Company Image +
+

Evany Store

+

Category: Shoes

+
+ + + +

5.0 Deliver on time

+
+ + + +
+
+ +
+ Sneaker Product + Sneaker Product + Sneaker Product +
+
+ +
+
+ Company Image +
+

Evany Store

+

Category: Shoes

+
+ + + +

5.0 Deliver on time

+
+ + + +
+
+ +
+ Sneaker Product + Sneaker Product + Sneaker Product +
+
+
+
+
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/app/home.html b/templates/views/app/home.html new file mode 100644 index 0000000..195b1ab --- /dev/null +++ b/templates/views/app/home.html @@ -0,0 +1,54 @@ + + + + + + {"Home"} + + + +
+ + Logo Fuente + + +
+
+ + + + +
+ + + + + + + + + + + + +
+
+ + + + \ No newline at end of file diff --git a/templates/views/app/orders.html b/templates/views/app/orders.html new file mode 100644 index 0000000..7df18be --- /dev/null +++ b/templates/views/app/orders.html @@ -0,0 +1,158 @@ + + + + + + {"My orders"} + + + +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+

{"My Orders"}

+
+ + +
+ + + + + + + + + + + + + + + + + + + + +
{"Order Number"}{"Purchased Date"}{"Product Details"}{"Driver Assigned"}{"Status of Store"}
{"#1234567"}{"12/12/24"} +
+ Shoes Image +
+

{"Brand of shoes"}

+

{"Woman Rainbow shoes"}

+

{"CODE: 001212"}

+
+
+ +
+ Shoes Image +
+

{"Brand of shoes"}

+

{"Woman Rainbow shoes"}

+

{"CODE: 001212"}

+
+
+ +
+ Shoes Image +
+

{"Brand of shoes"}

+

{"Woman Rainbow shoes"}

+

{"CODE: 001212"}

+
+
+
{"John Doe"}{"Delivered"}
+
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/app/packages-track.html b/templates/views/app/packages-track.html new file mode 100644 index 0000000..3704448 --- /dev/null +++ b/templates/views/app/packages-track.html @@ -0,0 +1,160 @@ + + + + + + {"Packages - Track"} + + + +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+

{"Your Packages"}

+ + + + + + + + + + + + + + + + + + + + + +
{"Order Number"}{"Purchased Date"}{"Product Details"}{"Driver Assigned"}{"Time to Deliver"}
{"#1234567"}{"12/12/24"} +
+ Shoes Image +
+

{"Brand of shoes"}

+

{"Woman Rainbow shoes"}

+

{"CODE: 001212"}

+
+
+ +
+ Shoes Image +
+

{"Brand of shoes"}

+

{"Woman Rainbow shoes"}

+

{"CODE: 001212"}

+
+
+ +
+ Shoes Image +
+

{"Brand of shoes"}

+

{"Woman Rainbow shoes"}

+

{"CODE: 001212"}

+
+
+
{"John Doe"}{"3:30 PM"}
+ + Mapa image +
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/app/payment.html b/templates/views/app/payment.html new file mode 100644 index 0000000..ff49fbd --- /dev/null +++ b/templates/views/app/payment.html @@ -0,0 +1,111 @@ + + + + + + {"Payment"} + + + +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
+ +
+
+

{"1"}

+

{"Payment"}

+
+ +
+

{"2"}

+

{"Confirmation"}

+
+
+ +
+
+ +
+ +
+

{"Your transaction is waiting to be approved by the store."}

+

{"Please do not close the window, approval may take 5 minutes, otherwise your money will be refunded tho the account"}

+
+
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/app/profile-address.html b/templates/views/app/profile-address.html new file mode 100644 index 0000000..edecb27 --- /dev/null +++ b/templates/views/app/profile-address.html @@ -0,0 +1,146 @@ + + + + + + {Profile - Address} + + + +
+ + Logo Fuente + + +
+
+ + + + +
+ + + + + + + + + + + + +
+
+ + + +
+

{"My Profile"}

+ +
+
+ +
+
+ +
+
+
+ +
+
+ + +
+
+

{"Address Registered"}

+ {"Cali Street, New York, United States, 11232"} +
+ + +
+
+ +
+ +
+
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/app/profile-personal.html b/templates/views/app/profile-personal.html new file mode 100644 index 0000000..37faff1 --- /dev/null +++ b/templates/views/app/profile-personal.html @@ -0,0 +1,162 @@ + + + + + + {"Profile - Personal Information"} + + + +
+ + Logo Fuente + + +
+
+ + + + + +
+ + + + + + + + + + + + + + + + +
+
+ + + +
+

{"My Profile"}

+ +
+
+ +
+
+ +
+
+
+ +
+
+ + +
+
+

{"Christine Amador Russel"}

+

+ {"Email"} {"christineam@gmail.com"} +

+

+ {"Phone Number"} {"+507 7656559012"} +

+
+ + +
+
+ +
+ +
+
+ +
+
+ +

{"Fuente"}

+
+ +
+

{"About Fuente"}

+
+

> {"How to buy?"}

+

> {"How to sale?"}

+

> {"Why is secure?"}

+
+
+ +
+

{"Benefits of Fuente"}

+
+

> {"Our benefits"}

+

> {"Shipping and collections of orders"}

+

> {"Payment methods"}

+
+
+ +
+

{"Politics of Fuente"}

+
+

> {"Terms and conditions"}

+

> {"General policies"}

+

> {"Privacy Policy"}

+

> {"Return and exchanges"}

+
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/auth/login.html b/templates/views/auth/login.html new file mode 100644 index 0000000..3da111a --- /dev/null +++ b/templates/views/auth/login.html @@ -0,0 +1,48 @@ + + + + + + + {"Login"} + + + +
+ + +
+

{"Now"}

+ +
+

{"Login"}

+
+
+ + +
+ + +
+
+
+
+ + \ No newline at end of file diff --git a/templates/views/auth/register.html b/templates/views/auth/register.html new file mode 100644 index 0000000..cf4ddbf --- /dev/null +++ b/templates/views/auth/register.html @@ -0,0 +1,73 @@ + + + + + + + {"Register"} + + + +
+ + +
+

{"Now"}

+ +
+

{"Register"}

+
+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ + +
+
+
+
+ + \ No newline at end of file