diff --git a/components/Account.js b/components/Account.js new file mode 100644 index 0000000..e01d7e1 --- /dev/null +++ b/components/Account.js @@ -0,0 +1,110 @@ +import { useState, useEffect } from 'react' +import { supabase } from '../utils/supabaseClient' + +export default function Account({ session }) { + const [loading, setLoading] = useState(true) + const [username, setUsername] = useState(null) + const [website, setWebsite] = useState(null) + const [avatar_url, setAvatarUrl] = useState(null) + + useEffect(() => { + getProfile() + }, [session]) + + async function getProfile() { + try { + setLoading(true) + const user = supabase.auth.user() + + let { data, error, status } = await supabase + .from('profiles') + .select(`username, website, avatar_url`) + .eq('id', user.id) + .single() + + if (error && status !== 406) { + throw error + } + + if (data) { + setUsername(data.username) + setWebsite(data.website) + setAvatarUrl(data.avatar_url) + } + } catch (error) { + alert(error.message) + } finally { + setLoading(false) + } + } + + async function updateProfile({ username, website, avatar_url }) { + try { + setLoading(true) + const user = supabase.auth.user() + + const updates = { + id: user.id, + username, + website, + avatar_url, + updated_at: new Date(), + } + + let { error } = await supabase.from('profiles').upsert(updates, { + returning: 'minimal', // Don't return the value after inserting + }) + + if (error) { + throw error + } + } catch (error) { + alert(error.message) + } finally { + setLoading(false) + } + } + + return ( +
+
+ + +
+
+ + setUsername(e.target.value)} + /> +
+
+ + setWebsite(e.target.value)} + /> +
+ +
+ +
+ +
+ +
+
+ ) +} diff --git a/components/Auth.js b/components/Auth.js new file mode 100644 index 0000000..57cc90f --- /dev/null +++ b/components/Auth.js @@ -0,0 +1,30 @@ +import { Auth, Typography, Button } from "@supabase/ui"; +import { supabase } from "utils/supabaseClient"; + +const { Text } = Typography; + +// Create a single supabase client for interacting with your database + +const Container = (props) => { + const { user } = Auth.useUser(); + if (user) + return ( + <> +

Hello {user.email}! 👋 You are already logged in

+ + + ); + return props.children; +}; + +export default function AuthComponent() { + return ( + + + + + + ); +} diff --git a/components/Footer.js b/components/Footer.js new file mode 100644 index 0000000..8d6a35a --- /dev/null +++ b/components/Footer.js @@ -0,0 +1,9 @@ +const Nav = () => { + return ( + + ); +}; + +export default Nav; diff --git a/components/Layout.js b/components/Layout.js new file mode 100644 index 0000000..8070c90 --- /dev/null +++ b/components/Layout.js @@ -0,0 +1,16 @@ +import Nav from "./Nav"; +import Footer from "./Footer"; + +const Layout = (props) => { + return ( +
+
+ ); +}; + +export default Layout; diff --git a/components/Nav.js b/components/Nav.js new file mode 100644 index 0000000..c7c4521 --- /dev/null +++ b/components/Nav.js @@ -0,0 +1,40 @@ +import { Menu } from "react-feather"; +import Link from "next/link"; +const Nav = () => { + const NavMenu = ( + <> + + Home + + + Dashboard + + + About + + + Contact + + + ); + + return ( + + ); +}; + +export default Nav; diff --git a/env.local.example b/env.local.example new file mode 100644 index 0000000..af068a4 --- /dev/null +++ b/env.local.example @@ -0,0 +1,2 @@ +NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL +NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY \ No newline at end of file diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..bcfa855 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "." + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e151c2a..3cf46a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,13 @@ "version": "0.1.0", "dependencies": { "@supabase/supabase-js": "^1.15.0", + "@supabase/ui": "^0.27.3", "daisyui": "^1.3.4", "next": "latest", + "next-seo": "^4.24.0", "react": "^17.0.1", - "react-dom": "^17.0.1" + "react-dom": "^17.0.1", + "react-feather": "^2.0.9" }, "devDependencies": { "autoprefixer": "^10.0.4", @@ -306,6 +309,19 @@ "@supabase/storage-js": "^1.2.1" } }, + "node_modules/@supabase/ui": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@supabase/ui/-/ui-0.27.3.tgz", + "integrity": "sha512-SO7TO/TIK6qziHUTmd2YpTmUtpBfYrN4Gx9tL7pYDHhfmmVApmYXmxEVexHAvHkSsxaK8yPER9KbMpwXx3ZwPQ==", + "dependencies": { + "lodash": "^4.17.20", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.1", + "react-dom": "^16.13.1 || ^17.0.1" + } + }, "node_modules/@types/node": { "version": "15.12.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", @@ -2197,6 +2213,16 @@ } } }, + "node_modules/next-seo": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/next-seo/-/next-seo-4.24.0.tgz", + "integrity": "sha512-9VQXfXAelhE+hAWzJ4azigQaW3FPX0kU0eYKFQXKsQjgY7AWtukjRGXls0oSIk8khhDJwmCt46EwsO9n5DDW6Q==", + "peerDependencies": { + "next": "^8.1.1-canary.54 || ^9.0.0 || ^10.0.0", + "react": "^16.0.0 || ^17.0.0", + "react-dom": "^16.0.0 || ^17.0.0" + } + }, "node_modules/next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -2984,6 +3010,17 @@ "react": "17.0.2" } }, + "node_modules/react-feather": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/react-feather/-/react-feather-2.0.9.tgz", + "integrity": "sha512-yMfCGRkZdXwIs23Zw/zIWCJO3m3tlaUvtHiXlW+3FH7cIT6fiK1iJ7RJWugXq7Fso8ZaQyUm92/GOOHXvkiVUw==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^16.8.6 || ^17" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -3995,6 +4032,15 @@ "@supabase/storage-js": "^1.2.1" } }, + "@supabase/ui": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@supabase/ui/-/ui-0.27.3.tgz", + "integrity": "sha512-SO7TO/TIK6qziHUTmd2YpTmUtpBfYrN4Gx9tL7pYDHhfmmVApmYXmxEVexHAvHkSsxaK8yPER9KbMpwXx3ZwPQ==", + "requires": { + "lodash": "^4.17.20", + "prop-types": "^15.7.2" + } + }, "@types/node": { "version": "15.12.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.2.tgz", @@ -5484,6 +5530,12 @@ } } }, + "next-seo": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/next-seo/-/next-seo-4.24.0.tgz", + "integrity": "sha512-9VQXfXAelhE+hAWzJ4azigQaW3FPX0kU0eYKFQXKsQjgY7AWtukjRGXls0oSIk8khhDJwmCt46EwsO9n5DDW6Q==", + "requires": {} + }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -6090,6 +6142,14 @@ "scheduler": "^0.20.2" } }, + "react-feather": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/react-feather/-/react-feather-2.0.9.tgz", + "integrity": "sha512-yMfCGRkZdXwIs23Zw/zIWCJO3m3tlaUvtHiXlW+3FH7cIT6fiK1iJ7RJWugXq7Fso8ZaQyUm92/GOOHXvkiVUw==", + "requires": { + "prop-types": "^15.7.2" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index b3351f5..c749adb 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,13 @@ }, "dependencies": { "@supabase/supabase-js": "^1.15.0", + "@supabase/ui": "^0.27.3", "daisyui": "^1.3.4", "next": "latest", + "next-seo": "^4.24.0", "react": "^17.0.1", - "react-dom": "^17.0.1" + "react-dom": "^17.0.1", + "react-feather": "^2.0.9" }, "devDependencies": { "autoprefixer": "^10.0.4", diff --git a/pages/_app.js b/pages/_app.js index 1040120..77508f3 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,7 +1,28 @@ -import 'tailwindcss/tailwind.css' +import "tailwindcss/tailwind.css"; +import { Auth } from "@supabase/ui"; +import { DefaultSeo } from "next-seo"; +import { supabase } from "utils/supabaseClient"; function MyApp({ Component, pageProps }) { - return + return ( + <> + + + + + + ); } -export default MyApp +export default MyApp; diff --git a/pages/dashboard.js b/pages/dashboard.js new file mode 100644 index 0000000..f3c674e --- /dev/null +++ b/pages/dashboard.js @@ -0,0 +1,41 @@ +import Head from "next/head"; +import { useState, useEffect } from "react"; +import { supabase } from "../utils/supabaseClient"; +import Auth from "../components/Auth"; +import Account from "../components/Account"; +import Layout from "components/Layout"; + +export default function Home() { + const [session, setSession] = useState(null); + + useEffect(() => { + setSession(supabase.auth.session()); + + supabase.auth.onAuthStateChange((_event, session) => { + setSession(session); + }); + }, []); + return ( +
+ + Dashboard + + + + +
+

Dashboard

+
+ {!session ? ( +
+ +
+ ) : ( + + )} +
+
+
+
+ ); +} diff --git a/pages/index.js b/pages/index.js index 6b67e91..20ff742 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,82 +1,31 @@ -import Head from 'next/head' +import Head from "next/head"; +import { useState, useEffect } from "react"; +import { supabase } from "../utils/supabaseClient"; +import Auth from "../components/Auth"; +import Account from "../components/Account"; +import Layout from "components/Layout"; export default function Home() { + const [session, setSession] = useState(null); + + useEffect(() => { + setSession(supabase.auth.session()); + + supabase.auth.onAuthStateChange((_event, session) => { + setSession(session); + }); + }, []); return ( -
+
Create Next App - + - -
-

- Welcome to{' '} - - Next.js! - + +

+ Supanextail Boilerplate

- -

- Get started by editing{' '} - - pages/index.js - -

- - -

- - +
- ) + ); } diff --git a/utils/supabaseClient.js b/utils/supabaseClient.js new file mode 100644 index 0000000..e3fcf7f --- /dev/null +++ b/utils/supabaseClient.js @@ -0,0 +1,6 @@ +import { createClient } from '@supabase/supabase-js' + +const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL +const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY + +export const supabase = createClient(supabaseUrl, supabaseAnonKey) \ No newline at end of file