feat: landing page + smart components
This commit is contained in:
118
components/landing/Features.vue
Normal file
118
components/landing/Features.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<p class="my-4 font-semibold tracking-widest text-center text-accent">
|
||||
FEATURES
|
||||
</p>
|
||||
<h3
|
||||
class="
|
||||
max-w-xl
|
||||
mt-4
|
||||
mb-8
|
||||
text-4xl
|
||||
font-bold
|
||||
leading-tight
|
||||
tracking-tight
|
||||
text-center
|
||||
transition
|
||||
text-secondaryDark
|
||||
"
|
||||
>
|
||||
A technology-first approach to payments and finance
|
||||
</h3>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div
|
||||
v-for="(feature, index) in features"
|
||||
:key="`feature-${index}`"
|
||||
class="
|
||||
inline-flex
|
||||
flex-col
|
||||
p-8
|
||||
transition
|
||||
border
|
||||
rounded-lg
|
||||
border-divider
|
||||
"
|
||||
>
|
||||
<i class="text-3xl material-icons text-accent">{{ feature.icon }}</i>
|
||||
<div class="flex-grow">
|
||||
<h2
|
||||
class="
|
||||
mt-4
|
||||
mb-2
|
||||
text-lg
|
||||
font-semibold
|
||||
transition
|
||||
text-secondaryDark
|
||||
"
|
||||
>
|
||||
{{ feature.title }}
|
||||
</h2>
|
||||
<p>
|
||||
{{ feature.description }}
|
||||
</p>
|
||||
<p class="mt-2">
|
||||
<NuxtLink :to="feature.link.target" class="link">
|
||||
{{ feature.link.title }}
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
features: [
|
||||
{
|
||||
icon: "offline_bolt",
|
||||
title: "SaaS Executives",
|
||||
description:
|
||||
"Unblock your barriers to new markets to sell software globally, to companies of all sizes, at all different price points, and increase your revenue growth with Kooli’s SaaS Commerce Platform.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "stars",
|
||||
title: "Product Managers",
|
||||
description:
|
||||
"Don’t let your billing stack hold you back. Everything you need to grow your SaaS business. Subscription billing, payments, taxes and more, in one unified platform.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "supervised_user_circle",
|
||||
title: "Creators",
|
||||
description:
|
||||
"Kooli is for artists and creators: writers, designers, software developers, musicians, educators, filmmakers, and anyone in-between. If you make stuff, you can sell that stuff.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "build_circle",
|
||||
title: "Developers",
|
||||
description:
|
||||
"Focus on building, not billing. Save the blood, sweat and tears for your software development, not your billing stack, with Paddle’s subscription and commerce platform.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "monetization_on",
|
||||
title: "Finance",
|
||||
description:
|
||||
"All your payments, subscriptions, taxes, invoices, SaaS metrics, and more in one place. Integrate your product, CRM, and accounting with only one tool, not dozens.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "group_work",
|
||||
title: "Enterprise",
|
||||
description:
|
||||
"Accept payments in 135+ currencies to better serve your international customers with a single integration. No international business entity required. Fees exclude GST.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
168
components/landing/Footer.vue
Normal file
168
components/landing/Footer.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<footer class="flex flex-col px-8 py-16">
|
||||
<nav class="grid grid-cols-2 gap-4 md:grid-cols-4">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<SmartLink
|
||||
:to="
|
||||
!$store.state.authUser
|
||||
? localePath('/')
|
||||
: localePath(`/${$store.state.login}`)
|
||||
"
|
||||
class="my-2"
|
||||
>
|
||||
<AppLogo class="h-8" />
|
||||
</SmartLink>
|
||||
<span> Kooli </span>
|
||||
<SmartChangeLanguage />
|
||||
<ul class="space-y-2">
|
||||
<li>
|
||||
<SmartAnchor label="Terms" to="/about/terms" class="footer-nav" />
|
||||
</li>
|
||||
<li>
|
||||
<SmartAnchor
|
||||
label="Privacy"
|
||||
to="/about/privacy"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<SmartColorModePicker />
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2 font-semibold">Solutions</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.solutions"
|
||||
:key="`item-${index}`"
|
||||
>
|
||||
<SmartAnchor
|
||||
:label="item.name"
|
||||
:to="item.link"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2 font-semibold">Platform</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.platform"
|
||||
:key="`item-${index}`"
|
||||
>
|
||||
<SmartAnchor
|
||||
:label="item.name"
|
||||
:to="item.link"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2 font-semibold">Company</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.company"
|
||||
:key="`item-${index}`"
|
||||
>
|
||||
<SmartAnchor
|
||||
:label="item.name"
|
||||
:to="item.link"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
navigation: {
|
||||
solutions: [
|
||||
{
|
||||
name: "SaaS",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Products",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Creators",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Developers",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Finance",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Enterprise",
|
||||
link: "/settings",
|
||||
},
|
||||
],
|
||||
platform: [
|
||||
{
|
||||
name: "Payments",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "Subscriptions",
|
||||
link: "/settings",
|
||||
},
|
||||
{
|
||||
name: "API",
|
||||
link: "https://docs.kooli.tech/api",
|
||||
},
|
||||
{
|
||||
name: "Guides",
|
||||
link: "https://docs.kooli.tech/guides",
|
||||
},
|
||||
],
|
||||
company: [
|
||||
{
|
||||
name: "About",
|
||||
link: "/about",
|
||||
},
|
||||
{
|
||||
name: "Jobs",
|
||||
link: "/about/jobs",
|
||||
},
|
||||
{
|
||||
name: "Integrations",
|
||||
link: "/about/integrations",
|
||||
},
|
||||
{
|
||||
name: "Support",
|
||||
link: "",
|
||||
},
|
||||
{
|
||||
name: "Contact",
|
||||
link: "/about/contact",
|
||||
},
|
||||
{
|
||||
name: "Blog",
|
||||
link: "https://blog.kooli.tech",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.footer-nav {
|
||||
&:hover,
|
||||
&:focus {
|
||||
@apply text-secondaryDark;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
97
components/landing/Globe.vue
Normal file
97
components/landing/Globe.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div ref="globe"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ThreeGlobe from "three-globe"
|
||||
import * as THREE from "three"
|
||||
import geojson from "~/assets/geojson/ne_110m_admin_0_countries.geojson"
|
||||
import texture from "~/assets/images/texture.png"
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
globe: null,
|
||||
cube: null,
|
||||
renderer: null,
|
||||
scene: null,
|
||||
camera: null,
|
||||
tbControls: null,
|
||||
arcsData: [...Array(40).keys()].map(() => ({
|
||||
startLat: (Math.random() - 0.5) * 180,
|
||||
startLng: (Math.random() - 0.5) * 360,
|
||||
endLat: (Math.random() - 0.5) * 180,
|
||||
endLng: (Math.random() - 0.5) * 360,
|
||||
color: ["#00acee", "#aceeff", "#00ffac", "#aaef3e"][
|
||||
Math.round(Math.random() * 3)
|
||||
],
|
||||
})),
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.init()
|
||||
this.animate()
|
||||
window.addEventListener(
|
||||
"resize",
|
||||
() => {
|
||||
this.camera.aspect =
|
||||
this.$refs.globe.clientWidth / this.$refs.globe.clientHeight
|
||||
this.camera.updateProjectionMatrix()
|
||||
this.renderer.setSize(
|
||||
this.$refs.globe.clientWidth,
|
||||
this.$refs.globe.clientHeight
|
||||
)
|
||||
},
|
||||
false
|
||||
)
|
||||
},
|
||||
|
||||
methods: {
|
||||
init() {
|
||||
this.globe = new ThreeGlobe()
|
||||
.globeImageUrl(texture)
|
||||
.atmosphereColor("#aaaaaa")
|
||||
.arcsData(this.arcsData)
|
||||
.arcColor("color")
|
||||
.arcDashLength(1)
|
||||
.arcDashGap(5)
|
||||
.arcStroke(1)
|
||||
.arcDashInitialGap(() => Math.random() * 5)
|
||||
.arcDashAnimateTime(2000)
|
||||
.hexPolygonsData(geojson.features)
|
||||
.hexPolygonResolution(3)
|
||||
.hexPolygonMargin(0.5)
|
||||
.hexPolygonColor(() => `#aaaaaa`)
|
||||
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
alpha: true,
|
||||
})
|
||||
this.renderer.setSize(
|
||||
this.$refs.globe.clientWidth,
|
||||
this.$refs.globe.clientHeight
|
||||
)
|
||||
|
||||
this.$refs.globe.appendChild(this.renderer.domElement)
|
||||
|
||||
this.scene = new THREE.Scene()
|
||||
this.scene.background = null
|
||||
this.scene.add(this.globe)
|
||||
this.scene.add(new THREE.AmbientLight(0xffffff))
|
||||
this.scene.add(new THREE.DirectionalLight(0xffffff, 0.8))
|
||||
|
||||
this.camera = new THREE.PerspectiveCamera()
|
||||
this.camera.aspect =
|
||||
this.$refs.globe.clientWidth / this.$refs.globe.clientHeight
|
||||
this.camera.updateProjectionMatrix()
|
||||
this.camera.position.z = 300
|
||||
},
|
||||
|
||||
animate() {
|
||||
this.renderer.render(this.scene, this.camera)
|
||||
requestAnimationFrame(this.animate)
|
||||
this.globe.rotation.y -= 0.005
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
42
components/landing/Hero.vue
Normal file
42
components/landing/Hero.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="flex p-4 relative overflow-hidden">
|
||||
<div class="relative my-16 z-10 max-w-3xl">
|
||||
<h2
|
||||
class="
|
||||
leading-none
|
||||
tracking-tighter
|
||||
font-semibold
|
||||
text-accent text-5xl
|
||||
md:text-6xl
|
||||
lg:text-8xl
|
||||
"
|
||||
>
|
||||
Open Source
|
||||
</h2>
|
||||
<h3
|
||||
class="
|
||||
text-3xl
|
||||
my-8
|
||||
font-mono
|
||||
text-secondaryDark
|
||||
md:text-4xl
|
||||
lg:text-4xl
|
||||
font-semibold
|
||||
"
|
||||
>
|
||||
API Development Ecosystem
|
||||
</h3>
|
||||
<p class="text-lg my-4 text-secondaryLight max-w-4/5">
|
||||
Millions of developers and companies build, ship, and maintain their
|
||||
APIs on Hoppscotch — the largest and most advanced development platform
|
||||
in the world.
|
||||
</p>
|
||||
<div class="my-8 flex items-center">
|
||||
<button class="button rounded text-xl">Get Started</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:absolute lg:inset-y-0 lg:right-0 lg:w-1/2">
|
||||
<LandingGlobe class="h-64 w-full sm:h-72 md:h-96 lg:w-full lg:h-full" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
142
components/landing/Package.vue
Normal file
142
components/landing/Package.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-4">
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<div
|
||||
class="inline-flex flex-col transition border rounded-lg border-divider"
|
||||
>
|
||||
<div class="flex flex-col p-8">
|
||||
<p class="font-semibold tracking-widest text-accent">API</p>
|
||||
<h3
|
||||
class="
|
||||
max-w-xl
|
||||
my-4
|
||||
text-4xl
|
||||
font-semibold
|
||||
leading-tight
|
||||
tracking-tight
|
||||
transition
|
||||
text-secondaryDark
|
||||
"
|
||||
>
|
||||
Powerful and easy-to-use APIs
|
||||
</h3>
|
||||
<p class="my-4 text-lg md:w-4/5">
|
||||
We agonize over the right abstractions so your teams don’t need to
|
||||
stitch together disparate systems or spend months integrating
|
||||
payments functionality.
|
||||
</p>
|
||||
<div class="flex mt-4">
|
||||
<ButtonPrimary
|
||||
to="https://docs.kooli.tech/guides"
|
||||
label="Read the docs"
|
||||
icon="chevron_right"
|
||||
reverse
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-4 lg:grid-cols-2">
|
||||
<div
|
||||
v-for="(api, index) in apis"
|
||||
:key="`api-${index}`"
|
||||
class="inline-flex flex-col p-8"
|
||||
>
|
||||
<i class="text-3xl material-icons text-accent">{{ api.icon }}</i>
|
||||
<div class="flex-grow">
|
||||
<h2
|
||||
class="
|
||||
mt-4
|
||||
mb-2
|
||||
text-lg
|
||||
font-semibold
|
||||
transition
|
||||
text-secondaryDark
|
||||
"
|
||||
>
|
||||
{{ api.title }}
|
||||
</h2>
|
||||
<p>
|
||||
{{ api.description }}
|
||||
</p>
|
||||
<p class="mt-2">
|
||||
<SmartLink :to="api.link.target" class="link">
|
||||
{{ api.link.title }}
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</SmartLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
inline-flex
|
||||
flex-col
|
||||
p-4
|
||||
transition
|
||||
border
|
||||
rounded-lg
|
||||
border-divider
|
||||
bg-primaryLight
|
||||
"
|
||||
>
|
||||
<div class="flex">
|
||||
<div class="w-3 h-3 mr-2 bg-red-500 rounded-full"></div>
|
||||
<div class="w-3 h-3 mr-2 bg-yellow-500 rounded-full"></div>
|
||||
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
|
||||
</div>
|
||||
<div class="flex flex-1 p-4 overflow-auto whitespace-pre">
|
||||
<pre>
|
||||
/*
|
||||
** TailwindCSS Configuration File
|
||||
**
|
||||
** Docs: https://tailwindcss.com/docs/configuration
|
||||
** Default: https://github.com/tailwindcss/tailwindcss/blob/master/stubs/defaultConfig.stub.js
|
||||
*/
|
||||
module.exports = {
|
||||
future: {
|
||||
removeDeprecatedGapUtilities: true,
|
||||
purgeLayersByDefault: true,
|
||||
},
|
||||
experimental: "all",
|
||||
dark: "class",
|
||||
corePlugins: {
|
||||
float: false,
|
||||
clear: false,
|
||||
transitionDelay: false,
|
||||
skew: false,
|
||||
},
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
apis: [
|
||||
{
|
||||
icon: "layers",
|
||||
title: "Tools for every stack",
|
||||
description:
|
||||
"We offer client and server libraries in everything from React, PHP to .NET and iOS.",
|
||||
link: {
|
||||
title: "See libraries",
|
||||
target: "https://docs.kooli.tech/api",
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "extension",
|
||||
title: "Prebuilt integrations",
|
||||
description:
|
||||
"Use integrations for systems like Shopify, PayPal, and more.",
|
||||
link: { title: "Explore partners", target: "/about/integrations" },
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
27
components/landing/Stats.vue
Normal file
27
components/landing/Stats.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="flex p-4 font-mono space-x-16">
|
||||
<div v-for="(stat, index) in stats" :key="`stat-${index}`">
|
||||
<span class="text-xl font-bold">
|
||||
{{ stat.count }}<span class="text-secondaryLight">+</span>
|
||||
</span>
|
||||
<br />
|
||||
<span class="text-sm">
|
||||
{{ stat.audience }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
stats: [
|
||||
{ count: "350k", audience: "Developers" },
|
||||
{ count: "10k", audience: "Organizations" },
|
||||
{ count: "1m", audience: "Requests" },
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
27
components/landing/Users.vue
Normal file
27
components/landing/Users.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<p
|
||||
class="mt-4 mb-8 font-semibold tracking-widest text-center text-accent"
|
||||
>
|
||||
EMPOWERING CREATORS
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-4 md:grid-cols-4 lg:grid-cols-6">
|
||||
<div
|
||||
v-for="(user, index) in 12"
|
||||
:key="`user-${index}`"
|
||||
class="inline-flex flex-col items-center justify-center p-4"
|
||||
>
|
||||
<img
|
||||
:src="`https://i.pravatar.cc/${Math.floor(
|
||||
Math.random() * (600 - 400 + 1) + 400
|
||||
)}`"
|
||||
alt="Profile picture"
|
||||
loading="lazy"
|
||||
class="inline-flex flex-col object-contain object-center h-24 w-24"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user