feat: add public site pages and social login
This commit is contained in:
@@ -45,23 +45,32 @@ export function useFacebookLogin() {
|
|||||||
const loginWithFacebook = () => {
|
const loginWithFacebook = () => {
|
||||||
if (!isSdkLoaded.value) {
|
if (!isSdkLoaded.value) {
|
||||||
console.error("Facebook SDK non encore chargé !");
|
console.error("Facebook SDK non encore chargé !");
|
||||||
|
return Promise.reject(new Error("Facebook SDK is not loaded"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
window.FB.login(
|
||||||
|
async (response) => {
|
||||||
|
if (!response.authResponse) {
|
||||||
|
console.log("Connexion annulée ou échouée.");
|
||||||
|
reject(new Error("Facebook login was cancelled or failed"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.FB.login(
|
try {
|
||||||
(response) => {
|
|
||||||
if (response.authResponse) {
|
|
||||||
console.log("Utilisateur connecté :", response);
|
console.log("Utilisateur connecté :", response);
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
authStore.loginWithFacebook(response.authResponse);
|
const result = await authStore.loginWithFacebook(response.authResponse);
|
||||||
} else {
|
resolve(result);
|
||||||
console.log("Connexion annulée ou échouée.");
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scope: "public_profile,email"
|
scope: "public_profile,email"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex min-h-full w-full items-center justify-center p-4">
|
<div class="login-page">
|
||||||
<div class="flex w-full max-w-[512px] flex-col gap-10">
|
<div class="login-wrap">
|
||||||
|
<router-link
|
||||||
|
class="login-brand"
|
||||||
|
to="/"
|
||||||
|
>
|
||||||
|
<span class="login-brand-mark">S</span>
|
||||||
|
<span class="login-brand-text">Socialize</span>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<section class="login-card">
|
||||||
<h1 class="login-text text-center text-2xl font-bold">
|
<h1 class="login-text text-center text-2xl font-bold">
|
||||||
{{ t('title') }}
|
{{ t('title') }}
|
||||||
</h1>
|
</h1>
|
||||||
@@ -18,6 +27,18 @@
|
|||||||
{{ t('continueWithGoogle') }}
|
{{ t('continueWithGoogle') }}
|
||||||
</button>
|
</button>
|
||||||
</google-login>
|
</google-login>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="secondary"
|
||||||
|
type="button"
|
||||||
|
@click="handleFacebookLogin"
|
||||||
|
>
|
||||||
|
<v-icon
|
||||||
|
:icon="mdiFacebook"
|
||||||
|
class="mr-2"
|
||||||
|
/>
|
||||||
|
{{ t('continueWithFacebook') }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="my-4 flex items-center">
|
<div class="my-4 flex items-center">
|
||||||
@@ -89,6 +110,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</v-form>
|
</v-form>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Error notification -->
|
<!-- Error notification -->
|
||||||
@@ -105,13 +127,15 @@
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { GoogleLogin } from 'vue3-google-login';
|
import { GoogleLogin } from 'vue3-google-login';
|
||||||
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
import { useAuthStore } from '@/features/auth/stores/authStore.js';
|
||||||
|
import { useFacebookLogin } from '@/features/auth/composables/useFacebookLogin.js';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { mdiEye, mdiEyeOff, mdiGoogle } from '@mdi/js';
|
import { mdiEye, mdiEyeOff, mdiFacebook, mdiGoogle } from '@mdi/js';
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
const { loginWithFacebook } = useFacebookLogin();
|
||||||
|
|
||||||
const email = ref('');
|
const email = ref('');
|
||||||
const password = ref('');
|
const password = ref('');
|
||||||
@@ -149,6 +173,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleFacebookLogin() {
|
||||||
|
try {
|
||||||
|
const response = await loginWithFacebook();
|
||||||
|
if (response === true) {
|
||||||
|
await router.push(props.returnUrl);
|
||||||
|
} else {
|
||||||
|
errorSnackBar.value = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Facebook login failed:', error);
|
||||||
|
errorSnackBar.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function forgotPassword() {
|
function forgotPassword() {
|
||||||
router.push('/forgot-password');
|
router.push('/forgot-password');
|
||||||
}
|
}
|
||||||
@@ -159,6 +197,41 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.login-page {
|
||||||
|
@apply flex min-h-screen w-full items-stretch justify-center sm:items-center sm:p-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-wrap {
|
||||||
|
@apply flex min-h-screen w-full max-w-[512px] flex-col gap-6 sm:min-h-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-brand {
|
||||||
|
@apply mx-auto flex items-center gap-3 px-4 pt-6 no-underline sm:px-0 sm:pt-0;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-brand-mark {
|
||||||
|
@apply flex h-11 w-11 items-center justify-center rounded-2xl text-lg font-black;
|
||||||
|
background: linear-gradient(135deg, #ff8a3d 0%, #ef4444 100%);
|
||||||
|
color: #fffaf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-brand-text {
|
||||||
|
@apply text-lg font-black uppercase tracking-[0.18em];
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-card {
|
||||||
|
@apply flex min-h-0 w-full flex-1 flex-col justify-center gap-10 bg-white/80 px-5 py-8 sm:flex-none sm:rounded-[1.5rem] sm:border sm:p-8;
|
||||||
|
border-color: rgba(23, 32, 51, 0.08);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.login-card {
|
||||||
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.visibility-toggle {
|
.visibility-toggle {
|
||||||
@apply cursor-pointer;
|
@apply cursor-pointer;
|
||||||
@apply transition-opacity duration-300;
|
@apply transition-opacity duration-300;
|
||||||
@@ -194,7 +267,8 @@
|
|||||||
"noAccount": "Don't have an account?",
|
"noAccount": "Don't have an account?",
|
||||||
"register": "Register",
|
"register": "Register",
|
||||||
"loginFailed": "Login failed. Please check your credentials.",
|
"loginFailed": "Login failed. Please check your credentials.",
|
||||||
"continueWithGoogle": "Continue with Google"
|
"continueWithGoogle": "Continue with Google",
|
||||||
|
"continueWithFacebook": "Continue with Facebook"
|
||||||
},
|
},
|
||||||
"fr": {
|
"fr": {
|
||||||
"title": "Se connecter",
|
"title": "Se connecter",
|
||||||
@@ -208,7 +282,8 @@
|
|||||||
"noAccount": "Vous n'avez pas de compte?",
|
"noAccount": "Vous n'avez pas de compte?",
|
||||||
"register": "S'inscrire",
|
"register": "S'inscrire",
|
||||||
"loginFailed": "Échec de la connexion. Veuillez vérifier vos identifiants.",
|
"loginFailed": "Échec de la connexion. Veuillez vérifier vos identifiants.",
|
||||||
"continueWithGoogle": "Continuer avec Google"
|
"continueWithGoogle": "Continuer avec Google",
|
||||||
|
"continueWithFacebook": "Continuer avec Facebook"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</i18n>
|
</i18n>
|
||||||
|
|||||||
113
frontend/src/features/landing/components/LandingSiteMenu.vue
Normal file
113
frontend/src/features/landing/components/LandingSiteMenu.vue
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<template>
|
||||||
|
<header class="site-menu">
|
||||||
|
<div class="site-menu-inner">
|
||||||
|
<router-link
|
||||||
|
class="site-brand"
|
||||||
|
to="/"
|
||||||
|
>
|
||||||
|
<span class="site-brand-mark">S</span>
|
||||||
|
<span class="site-brand-text">Socialize</span>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
<nav
|
||||||
|
class="site-nav"
|
||||||
|
aria-label="Public site navigation"
|
||||||
|
>
|
||||||
|
<router-link to="/product">Product</router-link>
|
||||||
|
<router-link to="/pricing">Pricing</router-link>
|
||||||
|
<div class="site-nav-group">
|
||||||
|
<button type="button">Resources</button>
|
||||||
|
<div class="site-nav-menu">
|
||||||
|
<router-link to="/blogs">Blogs</router-link>
|
||||||
|
<router-link to="/guides">Guides</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<router-link
|
||||||
|
class="site-login"
|
||||||
|
to="/login"
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.site-menu {
|
||||||
|
@apply sticky top-0 z-30 w-full;
|
||||||
|
background: rgba(255, 250, 242, 0.9);
|
||||||
|
backdrop-filter: blur(18px);
|
||||||
|
border-bottom: 1px solid rgba(23, 32, 51, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-menu-inner {
|
||||||
|
@apply mx-auto flex w-full max-w-7xl items-center justify-between gap-4 px-5 py-4 md:px-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-brand {
|
||||||
|
@apply flex min-w-0 items-center gap-3 no-underline;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-brand-mark {
|
||||||
|
@apply flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-2xl text-base font-black;
|
||||||
|
background: linear-gradient(135deg, #ff8a3d 0%, #ef4444 100%);
|
||||||
|
color: #fffaf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-brand-text {
|
||||||
|
@apply truncate text-lg font-black uppercase tracking-[0.18em];
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav {
|
||||||
|
@apply hidden items-center justify-center gap-2 sm:flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav a {
|
||||||
|
@apply rounded-full px-4 py-2 text-sm font-semibold no-underline transition-colors;
|
||||||
|
color: #44516a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav a:hover,
|
||||||
|
.site-nav-group:hover > button {
|
||||||
|
background: rgba(23, 32, 51, 0.06);
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav-group {
|
||||||
|
@apply relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav-group > button {
|
||||||
|
@apply rounded-full px-4 py-2 text-sm font-semibold transition-colors;
|
||||||
|
color: #44516a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav-menu {
|
||||||
|
@apply invisible absolute left-0 top-[calc(100%+0.5rem)] flex min-w-36 flex-col gap-1 rounded-[1rem] border p-2 opacity-0 transition-opacity;
|
||||||
|
background: rgba(255, 255, 255, 0.98);
|
||||||
|
border-color: rgba(23, 32, 51, 0.08);
|
||||||
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav-group:hover .site-nav-menu,
|
||||||
|
.site-nav-group:focus-within .site-nav-menu {
|
||||||
|
@apply visible opacity-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-nav-menu a {
|
||||||
|
@apply rounded-[0.75rem] px-3 py-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-login {
|
||||||
|
@apply flex h-10 items-center rounded-full px-4 text-sm font-bold no-underline transition-colors;
|
||||||
|
background: #172033;
|
||||||
|
color: #fffaf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-login:hover {
|
||||||
|
background: #0f766e;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
52
frontend/src/features/landing/views/BlogsPage.vue
Normal file
52
frontend/src/features/landing/views/BlogsPage.vue
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<script setup>
|
||||||
|
import LandingSiteMenu from '@/features/landing/components/LandingSiteMenu.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="public-page">
|
||||||
|
<LandingSiteMenu />
|
||||||
|
|
||||||
|
<main class="public-page-content">
|
||||||
|
<section class="public-page-panel">
|
||||||
|
<div class="eyebrow">Blogs</div>
|
||||||
|
<h1>Practical notes on content review workflows.</h1>
|
||||||
|
<p>
|
||||||
|
Articles are coming soon. This area will cover approval operations, client review habits,
|
||||||
|
revision tracking, and publication handoff.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.public-page {
|
||||||
|
@apply min-h-screen w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-page-content {
|
||||||
|
@apply mx-auto flex w-full max-w-7xl flex-col px-5 py-8 md:px-8 md:py-12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-page-panel {
|
||||||
|
@apply rounded-[2rem] p-6 md:p-10;
|
||||||
|
background: rgba(255, 255, 255, 0.84);
|
||||||
|
border: 1px solid rgba(23, 32, 51, 0.08);
|
||||||
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
@apply text-xs font-bold uppercase tracking-[0.26em];
|
||||||
|
color: #ff8a3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@apply mt-4 max-w-4xl text-4xl font-black leading-tight md:text-6xl;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
@apply mt-5 max-w-3xl text-base leading-7 md:text-lg;
|
||||||
|
color: #44516a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
52
frontend/src/features/landing/views/GuidesPage.vue
Normal file
52
frontend/src/features/landing/views/GuidesPage.vue
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<script setup>
|
||||||
|
import LandingSiteMenu from '@/features/landing/components/LandingSiteMenu.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="public-page">
|
||||||
|
<LandingSiteMenu />
|
||||||
|
|
||||||
|
<main class="public-page-content">
|
||||||
|
<section class="public-page-panel">
|
||||||
|
<div class="eyebrow">Guides</div>
|
||||||
|
<h1>Reusable guides for managing review and approval work.</h1>
|
||||||
|
<p>
|
||||||
|
Guides are coming soon. This area will collect repeatable playbooks for content intake,
|
||||||
|
review rounds, approval decisions, and delivery readiness.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.public-page {
|
||||||
|
@apply min-h-screen w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-page-content {
|
||||||
|
@apply mx-auto flex w-full max-w-7xl flex-col px-5 py-8 md:px-8 md:py-12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-page-panel {
|
||||||
|
@apply rounded-[2rem] p-6 md:p-10;
|
||||||
|
background: rgba(255, 255, 255, 0.84);
|
||||||
|
border: 1px solid rgba(23, 32, 51, 0.08);
|
||||||
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
@apply text-xs font-bold uppercase tracking-[0.26em];
|
||||||
|
color: #ff8a3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@apply mt-4 max-w-4xl text-4xl font-black leading-tight md:text-6xl;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
@apply mt-5 max-w-3xl text-base leading-7 md:text-lg;
|
||||||
|
color: #44516a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import LandingSiteMenu from '@/features/landing/components/LandingSiteMenu.vue';
|
||||||
|
|
||||||
const pillars = computed(() => [
|
const pillars = computed(() => [
|
||||||
{
|
{
|
||||||
@@ -25,8 +26,14 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="landing-shell">
|
<div class="landing-page">
|
||||||
<section class="hero-card">
|
<LandingSiteMenu />
|
||||||
|
|
||||||
|
<main class="landing-shell">
|
||||||
|
<section
|
||||||
|
id="products"
|
||||||
|
class="hero-card"
|
||||||
|
>
|
||||||
<div class="hero-copy">
|
<div class="hero-copy">
|
||||||
<div class="eyebrow">Social media approval workflow</div>
|
<div class="eyebrow">Social media approval workflow</div>
|
||||||
<h1>Replace Drive links, scattered comments, and manual follow-up with one review system.</h1>
|
<h1>Replace Drive links, scattered comments, and manual follow-up with one review system.</h1>
|
||||||
@@ -88,10 +95,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section
|
||||||
|
id="pricing"
|
||||||
|
class="pricing-card"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Pricing</div>
|
||||||
|
<h2>Workspace pricing for teams that manage content approvals with clients.</h2>
|
||||||
|
</div>
|
||||||
|
<router-link to="/login">
|
||||||
|
<button class="secondary pricing-action">Open the app</button>
|
||||||
|
</router-link>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.landing-page {
|
||||||
|
@apply min-h-screen w-full;
|
||||||
|
}
|
||||||
|
|
||||||
.landing-shell {
|
.landing-shell {
|
||||||
@apply mx-auto flex w-full max-w-7xl flex-col gap-8 px-5 py-8 md:px-8 md:py-12;
|
@apply mx-auto flex w-full max-w-7xl flex-col gap-8 px-5 py-8 md:px-8 md:py-12;
|
||||||
}
|
}
|
||||||
@@ -189,4 +214,20 @@
|
|||||||
@apply mt-2 block text-sm leading-6;
|
@apply mt-2 block text-sm leading-6;
|
||||||
color: #3f4d63;
|
color: #3f4d63;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pricing-card {
|
||||||
|
@apply flex flex-col gap-5 rounded-[1.75rem] p-6 md:flex-row md:items-center md:justify-between md:p-8;
|
||||||
|
background: rgba(255, 255, 255, 0.84);
|
||||||
|
border: 1px solid rgba(23, 32, 51, 0.08);
|
||||||
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pricing-card h2 {
|
||||||
|
@apply mt-3 max-w-3xl text-2xl font-black leading-tight md:text-3xl;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pricing-action {
|
||||||
|
@apply w-full sm:w-auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
68
frontend/src/features/landing/views/PricingPage.vue
Normal file
68
frontend/src/features/landing/views/PricingPage.vue
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<script setup>
|
||||||
|
import LandingSiteMenu from '@/features/landing/components/LandingSiteMenu.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="public-page">
|
||||||
|
<LandingSiteMenu />
|
||||||
|
|
||||||
|
<main class="public-page-content">
|
||||||
|
<section class="public-page-panel">
|
||||||
|
<div class="eyebrow">Pricing</div>
|
||||||
|
<h1>Simple workspace pricing for teams managing client approvals.</h1>
|
||||||
|
<p>
|
||||||
|
Pricing details are coming soon. For now, Socialize is focused on the core review workflow:
|
||||||
|
workspaces, content items, assets, comments, and approval decisions.
|
||||||
|
</p>
|
||||||
|
<router-link
|
||||||
|
class="pricing-login"
|
||||||
|
to="/login"
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</router-link>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.public-page {
|
||||||
|
@apply min-h-screen w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-page-content {
|
||||||
|
@apply mx-auto flex w-full max-w-7xl flex-col px-5 py-8 md:px-8 md:py-12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-page-panel {
|
||||||
|
@apply rounded-[2rem] p-6 md:p-10;
|
||||||
|
background: rgba(255, 255, 255, 0.84);
|
||||||
|
border: 1px solid rgba(23, 32, 51, 0.08);
|
||||||
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
@apply text-xs font-bold uppercase tracking-[0.26em];
|
||||||
|
color: #ff8a3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@apply mt-4 max-w-4xl text-4xl font-black leading-tight md:text-6xl;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
@apply mt-5 max-w-3xl text-base leading-7 md:text-lg;
|
||||||
|
color: #44516a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pricing-login {
|
||||||
|
@apply mt-8 inline-flex h-11 w-fit items-center rounded-full px-5 text-sm font-bold no-underline transition-colors;
|
||||||
|
background: #172033;
|
||||||
|
color: #fffaf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pricing-login:hover {
|
||||||
|
background: #0f766e;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
52
frontend/src/features/landing/views/ProductPage.vue
Normal file
52
frontend/src/features/landing/views/ProductPage.vue
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<script setup>
|
||||||
|
import LandingSiteMenu from '@/features/landing/components/LandingSiteMenu.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="public-page">
|
||||||
|
<LandingSiteMenu />
|
||||||
|
|
||||||
|
<main class="public-page-content">
|
||||||
|
<section class="public-page-panel">
|
||||||
|
<div class="eyebrow">Product</div>
|
||||||
|
<h1>Social media content approval, organized around the work itself.</h1>
|
||||||
|
<p>
|
||||||
|
Socialize keeps content items, assets, revisions, comments, approval decisions, and publishing
|
||||||
|
handoff details in one workspace so teams do not have to coordinate review across scattered tools.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.public-page {
|
||||||
|
@apply min-h-screen w-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-page-content {
|
||||||
|
@apply mx-auto flex w-full max-w-7xl flex-col px-5 py-8 md:px-8 md:py-12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.public-page-panel {
|
||||||
|
@apply rounded-[2rem] p-6 md:p-10;
|
||||||
|
background: rgba(255, 255, 255, 0.84);
|
||||||
|
border: 1px solid rgba(23, 32, 51, 0.08);
|
||||||
|
box-shadow: 0 18px 40px rgba(23, 32, 51, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
@apply text-xs font-bold uppercase tracking-[0.26em];
|
||||||
|
color: #ff8a3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@apply mt-4 max-w-4xl text-4xl font-black leading-tight md:text-6xl;
|
||||||
|
color: #172033;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
@apply mt-5 max-w-3xl text-base leading-7 md:text-lg;
|
||||||
|
color: #44516a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,6 +3,10 @@ import { createRouter, createWebHistory } from 'vue-router';
|
|||||||
|
|
||||||
const LoginView = () => import('@/features/auth/views/LoginView.vue');
|
const LoginView = () => import('@/features/auth/views/LoginView.vue');
|
||||||
const Landing = () => import('@/features/landing/views/Landing.vue');
|
const Landing = () => import('@/features/landing/views/Landing.vue');
|
||||||
|
const ProductPage = () => import('@/features/landing/views/ProductPage.vue');
|
||||||
|
const PricingPage = () => import('@/features/landing/views/PricingPage.vue');
|
||||||
|
const BlogsPage = () => import('@/features/landing/views/BlogsPage.vue');
|
||||||
|
const GuidesPage = () => import('@/features/landing/views/GuidesPage.vue');
|
||||||
const RegisterView = () => import('@/features/auth/views/RegisterView.vue');
|
const RegisterView = () => import('@/features/auth/views/RegisterView.vue');
|
||||||
const ForgotPasswordView = () => import('@/features/auth/views/ForgotPasswordView.vue');
|
const ForgotPasswordView = () => import('@/features/auth/views/ForgotPasswordView.vue');
|
||||||
const ResetPasswordView = () => import('@/features/auth/views/ResetPasswordView.vue');
|
const ResetPasswordView = () => import('@/features/auth/views/ResetPasswordView.vue');
|
||||||
@@ -32,6 +36,26 @@ const routes = [
|
|||||||
name: 'landing',
|
name: 'landing',
|
||||||
component: Landing,
|
component: Landing,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/product',
|
||||||
|
name: 'product',
|
||||||
|
component: ProductPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/pricing',
|
||||||
|
name: 'pricing',
|
||||||
|
component: PricingPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/blogs',
|
||||||
|
name: 'blogs',
|
||||||
|
component: BlogsPage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/guides',
|
||||||
|
name: 'guides',
|
||||||
|
component: GuidesPage,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/app',
|
path: '/app',
|
||||||
redirect: { name: 'dashboard' },
|
redirect: { name: 'dashboard' },
|
||||||
|
|||||||
Reference in New Issue
Block a user