basic forum done
its not pretty, but its a basic outline.
This commit is contained in:
parent
fbb392188c
commit
4da9e49ee0
|
|
@ -8,6 +8,9 @@ use Illuminate\Foundation\Validation\ValidatesRequests;
|
|||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use App\Models\User;
|
||||
use App\Models\Category;
|
||||
use App\Models\Post;
|
||||
use App\Models\Reply;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
|
@ -34,6 +37,44 @@ class Controller extends BaseController
|
|||
return Response()->json(["data"=>$array]);
|
||||
}
|
||||
|
||||
public function fetchCategories() {
|
||||
|
||||
$categories = Category::get();
|
||||
|
||||
return Response()->json(["data"=>$categories]);
|
||||
}
|
||||
|
||||
public function fetchCategory($id) {
|
||||
|
||||
$category = Category::where('id', $id)->first();
|
||||
|
||||
if (!$category) {return Response()->json(false);}
|
||||
|
||||
$posts = $category->posts()->paginate(20);
|
||||
|
||||
foreach ($posts as &$post) {
|
||||
$post['creator'] = User::where('id', $post['creator_id'])->first();
|
||||
}
|
||||
|
||||
return Response()->json(["data"=>$category, "posts"=>$posts]);
|
||||
}
|
||||
|
||||
public function fetchPost($id) {
|
||||
|
||||
$post = Post::where('id', $id)->first();
|
||||
|
||||
if (!$post) {return Response()->json(false);}
|
||||
|
||||
$postA = $post->toArray();
|
||||
|
||||
$postA['creator'] = User::where('id', $postA['creator_id'])->first();;
|
||||
|
||||
$replies = $post->replies()->paginate(10);
|
||||
|
||||
return Response()->json(["post"=>$postA,"replies"=>$replies]);
|
||||
}
|
||||
|
||||
|
||||
public function logout(Request $request) {
|
||||
|
||||
$POST;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,16 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use App\Models\User;
|
||||
use App\Models\Post;
|
||||
use App\Models\Category;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Request;
|
||||
use Auth;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
|
|
@ -25,4 +34,38 @@ class HomeController extends Controller
|
|||
{
|
||||
return view('home');
|
||||
}
|
||||
|
||||
public function createPost() {
|
||||
|
||||
$data = Request::all();
|
||||
|
||||
$valid = Validator::make($data, [
|
||||
'title' => ['required', 'string', 'min:3', 'max:38'],
|
||||
'body' => ['required', 'string', 'min:3', 'max:380'],
|
||||
]);
|
||||
|
||||
if ($valid->stopOnFirstFailure()->fails()) {
|
||||
$error = $valid->errors()->first();
|
||||
$messages = $valid->messages()->get('*');
|
||||
return Response()->json(['message'=>$error, 'badInputs'=>[array_keys($messages)]]);
|
||||
}
|
||||
|
||||
if (!isset($_POST['creator_id'])) {return Response()->json(['message'=>'System error', 'badInputs'=>['title']]);}
|
||||
|
||||
$user = User::where('id', $_POST['creator_id'])->first();
|
||||
|
||||
if (!$user) {return Response()->json(['message'=>'User not found!', 'badInputs'=>['title']]);}
|
||||
|
||||
$post = new Post;
|
||||
$post->title = $_POST['title'];
|
||||
$post->body = $_POST['body'];
|
||||
$post->creator_id = $_POST['creator_id'];
|
||||
//will add category support later
|
||||
$post->category_id = 1;
|
||||
$post->category_type = 'App\Models\Category';
|
||||
$post->save();
|
||||
|
||||
return Response()->json('good');
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Category extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'categories';
|
||||
|
||||
function posts()
|
||||
{
|
||||
return $this->morphMany('App\Models\Post', 'category');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Post extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'posts';
|
||||
|
||||
function replies()
|
||||
{
|
||||
return $this->morphMany('App\Models\Reply', 'post');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Reply extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'replies';
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateCategoriesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('categories', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title');
|
||||
$table->string('description');
|
||||
$table->boolean('staffOnly')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('categories');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreatePostsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('posts', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('title');
|
||||
$table->string('body');
|
||||
$table->boolean('pinned')->default(false);
|
||||
$table->boolean('locked')->default(false);
|
||||
$table->string('creator_id');
|
||||
$table->string("category_id");
|
||||
$table->string("category_type");
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('posts');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateRepliesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('replies', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('body');
|
||||
$table->string('creator_id');
|
||||
$table->boolean('pinned')->default(false);
|
||||
$table->string("post_id");
|
||||
$table->string("post_type");
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('replies');
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
"mini-css-extract-plugin": "^2.4.3",
|
||||
"react-bootstrap": "^2.0.2",
|
||||
"react-google-recaptcha": "^2.1.0",
|
||||
"react-router-guards": "^1.0.2",
|
||||
"styled-components": "^5.3.1",
|
||||
"three": "^0.135.0"
|
||||
},
|
||||
|
|
@ -8932,6 +8933,19 @@
|
|||
"react": ">=15"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-guards": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-guards/-/react-router-guards-1.0.2.tgz",
|
||||
"integrity": "sha512-RuNR+1sa7M96Jc3/hQxYw1E1ItYRUGFn+MKSoHSYCoWm6/JneA0z7C5spC9211FlkVXm79nonNtGy7exHSyKbA==",
|
||||
"dependencies": {
|
||||
"tiny-invariant": "^1.0.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-router": ">=5.0.0",
|
||||
"react-router-dom": ">=5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-router/node_modules/isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
|
|
@ -10263,8 +10277,7 @@
|
|||
"node_modules/tiny-invariant": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
|
||||
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
|
||||
},
|
||||
"node_modules/tiny-warning": {
|
||||
"version": "1.0.3",
|
||||
|
|
@ -17998,6 +18011,14 @@
|
|||
"tiny-warning": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"react-router-guards": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-guards/-/react-router-guards-1.0.2.tgz",
|
||||
"integrity": "sha512-RuNR+1sa7M96Jc3/hQxYw1E1ItYRUGFn+MKSoHSYCoWm6/JneA0z7C5spC9211FlkVXm79nonNtGy7exHSyKbA==",
|
||||
"requires": {
|
||||
"tiny-invariant": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"react-three-fiber": {
|
||||
"version": "0.0.0-deprecated",
|
||||
"resolved": "https://registry.npmjs.org/react-three-fiber/-/react-three-fiber-0.0.0-deprecated.tgz",
|
||||
|
|
@ -19007,8 +19028,7 @@
|
|||
"tiny-invariant": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
|
||||
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw=="
|
||||
},
|
||||
"tiny-warning": {
|
||||
"version": "1.0.3",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
"mini-css-extract-plugin": "^2.4.3",
|
||||
"react-bootstrap": "^2.0.2",
|
||||
"react-google-recaptcha": "^2.1.0",
|
||||
"react-router-guards": "^1.0.2",
|
||||
"styled-components": "^5.3.1",
|
||||
"three": "^0.135.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const Navbar = (props) => {
|
|||
<>
|
||||
<nav className="navbar graphictoria-navbar fixed-top navbar-expand-md shadow-sm">
|
||||
<div className="container-sm">
|
||||
<NavLink className="navbar-brand" to="/">
|
||||
<NavLink className="navbar-brand" to={props.user? `/home` : `/`}>
|
||||
<img src="/images/logo.png" alt="Graphictoria" width="43" height="43" draggable="false"/>
|
||||
</NavLink>
|
||||
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#graphictoria-nav" aria-controls="graphictoria-nav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ export function CreateAccount(form)
|
|||
resolve({message: res.message, inputs: res.badInputs});
|
||||
return;
|
||||
}
|
||||
window.location.replace(`/`);
|
||||
resolve("good");
|
||||
}).catch(error=>{console.log(error);});
|
||||
|
||||
|
|
@ -46,7 +45,6 @@ export function LoginToAccount(form) {
|
|||
resolve({message: res.message, inputs: res.badInputs});
|
||||
return;
|
||||
}
|
||||
window.location.replace(`/`);
|
||||
resolve("good");
|
||||
}).catch(error=>{console.log(error);});
|
||||
|
||||
|
|
@ -54,6 +52,24 @@ export function LoginToAccount(form) {
|
|||
|
||||
}
|
||||
|
||||
export function CreateForum(form) {
|
||||
|
||||
const body = form;
|
||||
var badInputs = [];
|
||||
|
||||
return new Promise(async (resolve, reject)=>{
|
||||
axios.post(`${protocol}apis.${url}/api/create/forum`, body, {headers: {'X-CSRF-TOKEN': document.querySelector(`meta[name="csrf-token"]`).content, "X-Requested-With":"XMLHttpRequest"}}).then(data=>{
|
||||
const res = data.data;
|
||||
if (res.badInputs.length >= 1) {
|
||||
badInputs=res.badInputs;
|
||||
resolve({message: res.message, inputs: res.badInputs});
|
||||
return;
|
||||
}
|
||||
resolve("good");
|
||||
}).catch(error=>{console.log(error);});
|
||||
});
|
||||
}
|
||||
|
||||
export function LogoutOfAccount() {
|
||||
|
||||
const body = form;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useState, useEffect } from "react";
|
|||
import ReactDOM from "react-dom";
|
||||
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
|
||||
import { PageTransition } from '@steveeeie/react-page-transition';
|
||||
import { GuardProvider, GuardedRoute } from 'react-router-guards'
|
||||
|
||||
import Config from '../config.js';
|
||||
|
||||
|
|
@ -27,6 +28,10 @@ import { Copyright } from '../Pages/Legal/Copyright.js';
|
|||
import { Privacy } from '../Pages/Legal/Privacy.js';
|
||||
import { Terms } from '../Pages/Legal/Terms.js';
|
||||
import { getCookie } from '../helpers/utils.js';
|
||||
import Dashboard from '../pages/Dashboard.js';
|
||||
import Forum from '../pages/Forum.js';
|
||||
import Post from '../pages/Post.js';
|
||||
import CreatePost from '../pages/CreatePost.js';
|
||||
|
||||
axios.defaults.withCredentials = true
|
||||
|
||||
|
|
@ -39,46 +44,56 @@ const App = () => {
|
|||
const [user, setUser] = useState([]);
|
||||
|
||||
function updateBanners()
|
||||
{
|
||||
axios.get(`${protocol}apis.${url}/banners/data`)
|
||||
.then((response) => {
|
||||
var result = [];
|
||||
response.data.map(function(banner){
|
||||
result.push(<Banner type={banner.type} description={banner.text} dismissible={banner.dismissable} />);
|
||||
});
|
||||
setState({banners: result});
|
||||
{
|
||||
axios.get(`${protocol}apis.${url}/banners/data`)
|
||||
.then((response) => {
|
||||
var result = [];
|
||||
response.data.map(function(banner){
|
||||
result.push(<Banner type={banner.type} description={banner.text} dismissible={banner.dismissable} />);
|
||||
});
|
||||
}
|
||||
|
||||
function fetchUser() {
|
||||
const body = new FormData();
|
||||
body.append('token', encodeURIComponent(getCookie(`gtok`)));
|
||||
axios.post(`${protocol}apis.${url}/fetch/user`, body).then((res)=>{
|
||||
setUser(res.data.data);
|
||||
setState({banners: result});
|
||||
});
|
||||
return new Promise(async (resolve, reject)=>{
|
||||
resolve("good");
|
||||
});
|
||||
}
|
||||
|
||||
function updateOfflineStatus()
|
||||
{
|
||||
axios.get(`${protocol}apis.${url}/`)
|
||||
.then((response) => {
|
||||
if(state.maintenance == true)
|
||||
window.location.reload();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response)
|
||||
{
|
||||
if(error.response.status == 503)
|
||||
setState({maintenance: true, theme: 1});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setState({offlineFetched: true});
|
||||
});
|
||||
}
|
||||
|
||||
function fetchUser() {
|
||||
const body = new FormData();
|
||||
body.append('token', encodeURIComponent(getCookie(`gtok`)));
|
||||
axios.post(`${protocol}apis.${url}/fetch/user`, body).then((res)=>{
|
||||
setUser(res.data.data);
|
||||
});
|
||||
return new Promise(async (resolve, reject)=>{
|
||||
resolve("good");
|
||||
});
|
||||
}
|
||||
|
||||
function updateOfflineStatus()
|
||||
{
|
||||
axios.get(`${protocol}apis.${url}/`)
|
||||
.then((response) => {
|
||||
if(state.maintenance == true)
|
||||
window.location.reload();
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response)
|
||||
{
|
||||
if(error.response.status == 503)
|
||||
setState({maintenance: true, theme: 1});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setState({offlineFetched: true});
|
||||
});
|
||||
}
|
||||
|
||||
const authMiddleware = (to, from, next) => {
|
||||
if (to.meta.auth) {
|
||||
if (user) {next();}
|
||||
next.redirect(`/login`);
|
||||
}else if (to.meta.guest) {
|
||||
if (!user) {next();}
|
||||
next.redirect(`/home`);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(async ()=>{
|
||||
await fetchUser();
|
||||
|
|
@ -95,6 +110,7 @@ const App = () => {
|
|||
return (
|
||||
!state.loading?
|
||||
<Router>
|
||||
<GuardProvider guards={[authMiddleware]}>
|
||||
<Navbar maintenanceEnabled={state.maintenance} user={user} />
|
||||
{state.banners && state.banners.length >= 1 ? state.banners : null}
|
||||
|
||||
|
|
@ -113,17 +129,39 @@ const App = () => {
|
|||
<Route exact path="/legal/terms-of-service" component={Terms}/>
|
||||
{state.maintenance ? <Route path="*" component={Maintenance}/> : null}
|
||||
|
||||
<Route exact path="/" component={Home}/>
|
||||
<GuardedRoute exact path="/" meta={{guest: true}}>
|
||||
<Home user={user}/>
|
||||
</GuardedRoute>
|
||||
|
||||
<GuardedRoute exact path="/home" meta={{auth: true}}>
|
||||
<Dashboard user={user}/>
|
||||
</GuardedRoute>
|
||||
|
||||
<Route exact path="/forum">
|
||||
<Forum user={user}/>
|
||||
</Route>
|
||||
|
||||
<GuardedRoute exact path="/forum/post" meta={{auth: true}}>
|
||||
<CreatePost user={user}/>
|
||||
</GuardedRoute>
|
||||
|
||||
<Route exact path="/forum/category/:id">
|
||||
<Forum user={user}/>
|
||||
</Route>
|
||||
|
||||
<Route exact path="/forum/post/:id">
|
||||
<Post user={user}/>
|
||||
</Route>
|
||||
|
||||
<Route exact path="/login">
|
||||
<Auth location={user && user.id? null : location.pathname}/>
|
||||
</Route>
|
||||
<Route exact path="/register">
|
||||
<GuardedRoute exact path="/login" meta={{guest: true}}>
|
||||
<Auth location={user? null : location.pathname}/>
|
||||
</Route>
|
||||
<Route exact path="/passwordreset">
|
||||
</GuardedRoute>
|
||||
<GuardedRoute exact path="/register" meta={{guest: true}}>
|
||||
<Auth location={user? null : location.pathname}/>
|
||||
</Route>
|
||||
</GuardedRoute>
|
||||
<GuardedRoute exact path="/passwordreset" meta={{guest: true}}>
|
||||
<Auth location={user? null : location.pathname}/>
|
||||
</GuardedRoute>
|
||||
|
||||
<Route exact path="/games" component={Games}/>
|
||||
|
||||
|
|
@ -138,6 +176,7 @@ const App = () => {
|
|||
</PageTransition>
|
||||
);
|
||||
}}/>
|
||||
</GuardProvider>
|
||||
</Router>
|
||||
:
|
||||
<div className="gtoria-loader-center">
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
const Card = (props) => {
|
||||
export const Card = (props) => {
|
||||
return (
|
||||
<div className="container graphictoria-center-vh">
|
||||
<div className="card graphictoria-small-card shadow-sm">
|
||||
<div className="card-body text-center">
|
||||
<div className={`card-body ${props.padding? `p5r` : null} text-center`}>
|
||||
{ props.children }
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -15,7 +15,7 @@ const Card = (props) => {
|
|||
);
|
||||
};
|
||||
|
||||
const CardTitle = (props) => {
|
||||
export const CardTitle = (props) => {
|
||||
return (
|
||||
<>
|
||||
<h5 className="card-title fw-bold">{ props.children }</h5>
|
||||
|
|
@ -23,8 +23,3 @@ const CardTitle = (props) => {
|
|||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
Card,
|
||||
CardTitle
|
||||
};
|
||||
|
|
@ -47,6 +47,10 @@ class Auth extends React.Component {
|
|||
pageLabel = (<><i className="fas fa-question-circle"></i> RESET PASSWORD</>);
|
||||
pageContent = (<ForgotPasswordForm />);
|
||||
break;
|
||||
case `guest`:
|
||||
pageLabel = (<><i className="fas fa-question-circle"></i> YOU NEED TO BE A GUEST!</>);
|
||||
pageContent = (<div><div>Sorry, this page is for guests only!</div></div>);
|
||||
break;
|
||||
default:
|
||||
pageLabel = (<><i className={`"fas fa-question-circle"`}></i> YOU'RE LOGGED IN!</>);
|
||||
pageContent = (<div><div>Sorry, this page is for unauthenticated members only!</div></div>);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ const LoginForm = (props) => {
|
|||
setTimeout(()=>{setValidity({...validity, error: false, inputs: res.inputs});}, 4000);
|
||||
return;
|
||||
}
|
||||
window.location.replace(`/home`);
|
||||
window.location.reload();
|
||||
}).catch(error=>console.log(error));
|
||||
setWaitingForSubmission(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
// © XlXi 2021
|
||||
// Graphictoria 5
|
||||
|
||||
import React, {useState} from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import ReCAPTCHA from 'react-google-recaptcha';
|
||||
import { CreateAccount, LoginToAccount, CreateForum } from '../Helpers/Auth';
|
||||
import Loader from '../Components/Loader';
|
||||
import { getCookie } from '../helpers/utils';
|
||||
|
||||
const CreatePost = (props) => {
|
||||
|
||||
const [waitingForSubmission, setWaitingForSubmission] = useState(false);
|
||||
const [validity, setValidity] = useState({error: false, message: ``, inputs: []});
|
||||
const user = props.user;
|
||||
|
||||
async function SubmitForm(form)
|
||||
{
|
||||
form.append('creator_id', user.id);
|
||||
setWaitingForSubmission(true);
|
||||
await CreateForum(form).then(res=>{
|
||||
console.log(res);
|
||||
if (res != `good`) {
|
||||
setValidity({error: true, message:res.message, inputs: res.inputs});
|
||||
setTimeout(()=>{setValidity({...validity, error: false, inputs: res.inputs});}, 4000);
|
||||
return;
|
||||
}
|
||||
window.location.href=`/forum`;
|
||||
}).catch(error=>console.log(error));
|
||||
setWaitingForSubmission(false);
|
||||
}
|
||||
|
||||
return (
|
||||
waitingForSubmission ? <Loader/> :
|
||||
<div className={`flex column jcc alc w-100`}>
|
||||
<div className={`flex card card-body column alc w-40`}>
|
||||
<div className={`flex row`}>
|
||||
<div className="col-md-8 mb-2">
|
||||
{validity.error?
|
||||
<div className={`px-5 mb-10`}>
|
||||
<div className={`error-dialog`}>
|
||||
<p className={`mb-0`}>{validity.message}</p>
|
||||
</div>
|
||||
</div>
|
||||
: null}
|
||||
<form onSubmit={(e)=>{e.preventDefault();SubmitForm(new FormData(e.target));}} class="fs">
|
||||
<input type="username" className={`form-control mb-4 ${(validity.inputs.find(input=>input == `title`)? `is-invalid` : ``)}`} placeholder="Title" name="title"/>
|
||||
<textarea type="username" className={`form-control mb-4 ${(validity.inputs.find(input=>input == `body`)? `is-invalid` : ``)}`} placeholder="Body" name="body"></textarea>
|
||||
<div className="d-flex mb-3">
|
||||
<ReCAPTCHA
|
||||
sitekey="6LeyHsUbAAAAAJ9smf-als-hXqrg7a-lHZ950-fL"
|
||||
className="mx-auto"
|
||||
/>
|
||||
</div>
|
||||
<button className="btn btn-primary px-5" type={`submit`}>POST!</button><br/>
|
||||
</form>
|
||||
</div>
|
||||
<div className="col">
|
||||
<h5><bold>Read the rules before posting!</bold></h5>
|
||||
<p>Before you make a post, be sure to read the <Link to={`/forum/rules`}>rules</Link>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreatePost;
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
// © XlXi 2021
|
||||
// Graphictoria 5
|
||||
|
||||
import axios from 'axios';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useHistory } from "react-router-dom";
|
||||
|
||||
import Config from '../config.js';
|
||||
|
||||
import SetTitle from "../Helpers/Title.js";
|
||||
|
||||
import Loader from '../Components/Loader.js';
|
||||
|
||||
import { GenericErrorModal } from './Errors.js';
|
||||
import { Card, CardTitle } from '../Layouts/Card.js';
|
||||
|
||||
var url = Config.BaseUrl.replace('http://', '');
|
||||
var protocol = Config.Protocol;
|
||||
|
||||
const Dashboard = (props) => {
|
||||
|
||||
const [state, setState] = useState({offline: false, loading: true});
|
||||
const user = props.user;
|
||||
|
||||
useEffect(()=>{
|
||||
SetTitle(`Dashboard`);
|
||||
setState({...state, loading: false});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
state.loading
|
||||
?
|
||||
<Loader />
|
||||
:
|
||||
<div className={`row`}>
|
||||
<div className={`col`}>
|
||||
<Card className={`justify-content-center`} padding={true}>
|
||||
<CardTitle>Hello, {user.username}!</CardTitle>
|
||||
<p>[Avatar goes here]</p>
|
||||
</Card>
|
||||
</div>
|
||||
<div className={`col justify-content-center`}>
|
||||
<p>Lmao</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Dashboard;
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
// © XlXi 2021
|
||||
// Graphictoria 5
|
||||
|
||||
import axios from 'axios';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useHistory, useParams } from "react-router-dom";
|
||||
|
||||
import Config from '../config.js';
|
||||
|
||||
import SetTitle from "../Helpers/Title.js";
|
||||
|
||||
import Loader from '../Components/Loader.js';
|
||||
|
||||
import { GenericErrorModal } from './Errors.js';
|
||||
import { Card, CardTitle } from '../Layouts/Card.js';
|
||||
|
||||
var url = Config.BaseUrl.replace('http://', '');
|
||||
var protocol = Config.Protocol;
|
||||
|
||||
const Forum = (props) => {
|
||||
|
||||
var id = useParams().id;
|
||||
const [state, setState] = useState({offline: false, loading: true});
|
||||
const [categories, setCategoires] = useState([]);
|
||||
const [category, setCategory] = useState([]);
|
||||
const [posts, setPosts] = useState({posts: [], currentPage: 0, meta: []});
|
||||
const user = props.user;
|
||||
|
||||
if (!id) id = 1;
|
||||
|
||||
const fetchCategories = async () => {
|
||||
await axios.get(`${protocol}apis.${url}/fetch/categories`, {headers: {"X-Requested-With":"XMLHttpRequest"}}).then(data=>{
|
||||
setCategoires(data.data.data);
|
||||
}).catch(error=>{console.log(error);});
|
||||
}
|
||||
|
||||
const fetchCategory = async () => {
|
||||
await axios.get(`${protocol}apis.${url}/fetch/category/${id}`, {headers: {"X-Requested-With":"XMLHttpRequest"}}).then(data=>{
|
||||
if (!data.data) {window.location.href=`/forum`;return;}
|
||||
setCategory(data.data.data);
|
||||
setPosts({...posts, posts: data.data.posts.data, meta: data.data.posts});
|
||||
}).catch(error=>{console.log(error);});
|
||||
}
|
||||
|
||||
useEffect(async ()=>{
|
||||
SetTitle(`Forum`);
|
||||
await fetchCategories();
|
||||
await fetchCategory();
|
||||
setState({...state, loading: false});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
state.loading
|
||||
?
|
||||
<Loader />
|
||||
:
|
||||
<div className={`flex jcc alc w-100 column`}>
|
||||
<div class="jumbo w-60">
|
||||
<div class="container">
|
||||
<h1>Welcome to {category.title}!</h1>
|
||||
<p>{category.description}</p>
|
||||
{user?
|
||||
<div className={`flex row justify-content-center`}>
|
||||
<Link className={`btn btn-success w-20`} to={`/forum/post`}>Create a post</Link>
|
||||
</div>
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="graphictoria-nav-splitter"></div>
|
||||
<div className={`row w-60`}>
|
||||
<div className={`col-3 justify-content-center`}>
|
||||
<h4>Categories:</h4>
|
||||
{categories.map(category=>(
|
||||
<>
|
||||
<Link to={`/forum/category/${category.id}`}>{category.title}</Link><br/>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
<div className={`col justify-content-center`}>
|
||||
{posts.posts.length <= 0 ? <p>There are currently no posts!</p> : null}
|
||||
{posts.posts.map(post=>(
|
||||
<>
|
||||
<Link to={`/forum/post/${post.id}`} className={`flex graphic-post`}>
|
||||
<div className={`flex column mr-15 alc`}>
|
||||
[Avatar.]
|
||||
</div>
|
||||
<div className={`flex row m-0`}>
|
||||
<h5 className={`m-0`}>{post.title}</h5>
|
||||
<div className={`row fs12`}>
|
||||
<p>Posted by:</p>
|
||||
<Link to={`/user/${post.creator.id}`}>{post.creator.username}</Link>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Forum;
|
||||
|
|
@ -9,7 +9,7 @@ import SetTitle from "../Helpers/Title.js";
|
|||
import SocialCard from "../Components/Landing/SocialCard.js";
|
||||
import { user } from "../helpers/utils.js";
|
||||
|
||||
const Home = () => {
|
||||
const Home = (props) => {
|
||||
useEffect(()=>{
|
||||
SetTitle();
|
||||
}, [])
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
// © XlXi 2021
|
||||
// Graphictoria 5
|
||||
|
||||
import axios from 'axios';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useHistory, useParams } from "react-router-dom";
|
||||
|
||||
import Config from '../config.js';
|
||||
|
||||
import SetTitle from "../Helpers/Title.js";
|
||||
|
||||
import Loader from '../Components/Loader.js';
|
||||
|
||||
import { GenericErrorModal } from './Errors.js';
|
||||
import { Card, CardTitle } from '../Layouts/Card.js';
|
||||
|
||||
var url = Config.BaseUrl.replace('http://', '');
|
||||
var protocol = Config.Protocol;
|
||||
|
||||
const Post = (props) => {
|
||||
|
||||
var id = useParams().id;
|
||||
const [state, setState] = useState({offline: false, loading: true});
|
||||
const [post, setPost] = useState({post: [], replies: {replies: [], meta: [], currentPage: 0}});
|
||||
const user = props.user;
|
||||
|
||||
const fetchPost = async () => {
|
||||
await axios.get(`${protocol}apis.${url}/fetch/post/${id}`, {headers: {"X-Requested-With":"XMLHttpRequest"}}).then(data=>{
|
||||
if (!data.data) {window.location.href=`/forum`;return;}
|
||||
setPost({post: data.data.post, replies: {replies: data.data.replies.data, meta: data.data.replies, currentPage: 0}});
|
||||
}).catch(error=>{console.log(error);});
|
||||
}
|
||||
|
||||
useEffect(async ()=>{
|
||||
SetTitle(`Forum`);
|
||||
await fetchPost();
|
||||
setState({...state, loading: false});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
state.loading
|
||||
?
|
||||
<Loader />
|
||||
:
|
||||
<div className={`flex jcc alc w-100 column`}>
|
||||
<div className={`graphic-post w-40`}>
|
||||
{/* Time&Date goes here. */}
|
||||
<div className={`flex w-100`}>
|
||||
<div className={`flex column mr-15`}>
|
||||
<p>Posted by:</p>
|
||||
<Link to={`/user/${post.post.creator.id}`}>{post.post.creator.username}</Link>
|
||||
</div>
|
||||
<div className={`flex row`}>
|
||||
<p className={`m-0`}>{post.post.body}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Post;
|
||||
|
|
@ -823,6 +823,109 @@ a.list-group-item {
|
|||
color: transparent !important;
|
||||
}
|
||||
|
||||
.p5r {
|
||||
padding: .5rem .5rem !important;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.jcc {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.alc {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.w-90 {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.w-80 {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.w-70 {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.w-60 {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.w-50 {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.w-40 {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.w-30 {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.w-20 {
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.w-10 {
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.w-5 {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
.jumbo {
|
||||
background-color: #222;
|
||||
padding: 1.3rem 1.3rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
max-width: 60%;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.column {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mr-15 {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.m-0 {
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0px !important;
|
||||
}
|
||||
|
||||
.fs12 {
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
||||
.graphic-post {
|
||||
padding: 1rem 1rem;
|
||||
text-align: start;
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
background-color: #222 !important;
|
||||
border-radius: 0.25px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.error-dialog {
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,14 @@ Route::get('/banners/data', 'BannerController@getBanners');
|
|||
|
||||
Route::get('/games/metadata', 'GamesController@isAvailable');
|
||||
|
||||
Route::get('/fetch/categories', 'Controller@fetchCategories');
|
||||
|
||||
Route::get('/fetch/category/{id}', 'Controller@fetchCategory');
|
||||
|
||||
Route::get('/fetch/posts/{id}', 'Controller@fetchPosts');
|
||||
|
||||
Route::get('/fetch/post/{id}', 'Controller@fetchPost');
|
||||
|
||||
Route::post('/fetch/user', 'Controller@fetchUser');
|
||||
|
||||
Route::post('/maintenance/bypass', 'MaintenanceController@bypass');
|
||||
|
|
@ -35,6 +43,8 @@ Route::post('/account/register', 'Auth\RegisterController@create');
|
|||
|
||||
Route::post('/account/login', 'Controller@login');
|
||||
|
||||
Route::post('/api/create/forum', 'HomeController@createPost');
|
||||
|
||||
Route::fallback(function(){
|
||||
return response('{"errors":[{"code":404,"message":"NotFound"}]}', 404)
|
||||
->header('Cache-Control', 'private')
|
||||
|
|
|
|||
|
|
@ -21,6 +21,14 @@ Route::get('/', function(){
|
|||
return view('main');
|
||||
});
|
||||
|
||||
Route::get('/home', function(){
|
||||
return view('main');
|
||||
});
|
||||
|
||||
Route::get('/forum', function(){
|
||||
return view('main');
|
||||
});
|
||||
|
||||
Route::get('/login', function(){
|
||||
return view('main');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue