items & settings

built buying limiteds & selling limiteds. also buying from other users. also added changing passwords and emails.
This commit is contained in:
xander 2022-04-03 18:37:52 -12:00
parent a7690eada2
commit ae22ea9d5f
27 changed files with 12151 additions and 107 deletions

View File

@ -6,6 +6,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use App\Helpers\AuthHelper;
use Auth;
use App\Models\User;
class AuthController extends Controller
@ -74,7 +75,7 @@ class AuthController extends Controller
/* */
$data = $request->all();
$valid = Validator::make(
$data,
[
@ -96,7 +97,7 @@ class AuthController extends Controller
if (!$user)
return Response()->json(['message'=>'That user doesn\'t exist.', 'badInputs'=>['username']]);
if (Hash::check($request->input('password'), $user->password))
if (!Hash::check($request->input('password'), $user->password))
return Response()->json(['message'=>'The password you tried is incorrect.', 'badInputs'=>['password']]);
$request->session()->regenerate();

View File

@ -0,0 +1,246 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use App\Models\User\UserSession;
use App\Models\Post;
use App\Models\Reply;
use App\Models\Category;
use App\Models\Friend;
use App\Models\Feed;
use App\Models\Item;
use App\Models\Selling;
use App\Models\Inventory;
use App\Models\Staff;
use App\Models\Prices;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use App\Helpers\AuthHelper;
use Illuminate\Http\Request;
use Auth;
class CatalogController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
/*public function __construct()
{
$this->middleware('auth');
}*/
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
return view('home');
}
public function buy(Request $request, $id) {
$user = AuthHelper::GetCurrentUser($request);
if (!$user) return Response()->json(['message'=>'System Error', 'badInputs'=>['title']]);
$item = Item::whereId($id)->first();
if (!$item) return Response()->json(['message'=>'No Item.', 'badInputs'=>['title']]);
if (!isset($_POST['decision'])) return Response()->json(['message'=>'System Error', 'badInputs'=>['title']]);
$decision = $_POST['decision'];
if ($item->current_price > $user->bank) return Response()->json(['message'=>"Sorry, you don't have enough currency!", 'badInputs'=>['title']]);
switch($decision) {
case 'nonLimited':
$newInventory = new Inventory;
$newInventory->item_id = $item->id;
$newInventory->owner_id = $user->id;
$newInventory->owner_type = 'App\Models\User';
$newInventory->status = 1;
$user->inventory()->save($newInventory);
$newInventory->uid = 'nonLimited';
$newInventory->save();
$user->decrement('bank', $item->current_price);
$user->save();
$replies = $item->sellingPrices()->orderBy('price', 'asc')->paginate(10);
$itemA = $item->toArray();
$itemA['creator'] = User::where('id', $item->creator_id)->first();
foreach ($replies as &$reply) {
$creator = User::where('id', $reply['seller_id'])->first();
$reply['created_at'] = explode('T', $reply['created_at'])[0];
$reply['seller_name'] = $creator->username;
}
return Response()->json(['message'=>"Success!", 'badInputs'=>[], "item"=>$itemA, "sellingPrices"=>$replies]);
break;
case 'limited':
if ($item->stock <= 0) return Response()->json(['message'=>"Sorry, there's no more in stock for this item!", 'badInputs'=>['title']]);
$newInventory = new Inventory;
$newInventory->item_id = $item->id;
$newInventory->owner_id = $user->id;
$newInventory->owner_type = 'App\Models\User';
$newInventory->status = 1;
$user->inventory()->save($newInventory);
$newInventory->uid = $newInventory->id;
$newInventory->save();
$user->decrement('bank', $item->current_price);
$user->save();
$item->decrement('stock');
$item->save();
$replies = $item->sellingPrices()->orderBy('price', 'asc')->paginate(10);
$itemA = $item->toArray();
$itemA['creator'] = User::where('id', $item->creator_id)->first();
foreach ($replies as &$reply) {
$creator = User::where('id', $reply['seller_id'])->first();
$reply['created_at'] = explode('T', $reply['created_at'])[0];
$reply['seller_name'] = $creator->username;
}
return Response()->json(['message'=>"Success!", 'badInputs'=>[], "item"=>$itemA, "sellingPrices"=>$replies]);
break;
case 'selling':
if (!isset($_POST['sellingId'])) return Response()->json(['message'=>'No Selling ID.', 'badInputs'=>['title']]);
$sellingId = $_POST['sellingId'];
$sellingItem = Selling::whereId($sellingId)->first();
if (!$sellingItem) return Response()->json(['message'=>"That selling item doesn't exist!", 'badInputs'=>['title']]);
if ($sellingItem->seller_id == $user->id) return Response()->json(['message'=>"Thats you!", 'badInputs'=>['title']]);
$seller = User::where('id', $sellingItem->seller_id)->first();
$ownedItem = Inventory::where('owner_id', $sellingItem->seller_id)->where('item_id', $item->id)->first();
if ($sellingItem->price > $user->bank) return Response()->json(['message'=>"Sorry, you don't have enough currency!", 'badInputs'=>['title']]);
$newInventory = new Inventory;
$newInventory->item_id = $item->id;
$newInventory->owner_id = $user->id;
$newInventory->owner_type = 'App\Models\User';
$newInventory->status = 1;
$user->inventory()->save($newInventory);
$newInventory->uid = $sellingItem->uid;
$newInventory->save();
$user->decrement('bank', $sellingItem->price);
$user->save();
$seller->increment('bank', $sellingItem->price);
$seller->save();
/*$priceNew = new Prices;
$priceNew->price = $sellingItem->price;
$item->prices()->save($priceNew);*/
$sellingItem->delete();
$ownedItem->delete();
$sellingItemNew = Selling::whereId($sellingId)->first();
if (count($item->sellingPrices) <= 0) {$item->current_price = null;$item->save();}else{$item->current_price = $sellingItemNew->price;$item->save();}
$replies = $item->sellingPrices()->orderBy('price', 'asc')->paginate(10);
$itemA = $item->toArray();
$itemA['creator'] = User::where('id', $item->creator_id)->first();
foreach ($replies as &$reply) {
$creator = User::where('id', $reply['seller_id'])->first();
$reply['created_at'] = explode('T', $reply['created_at'])[0];
$reply['seller_name'] = $creator->username;
}
return Response()->json(['message'=>"Success!", 'badInputs'=>[], "item"=>$itemA, "sellingPrices"=>$replies]);
break;
default:
break;
}
}
public function sell(Request $request, $id) {
$user = AuthHelper::GetCurrentUser($request);
if (!$user) return Response()->json(['message'=>'System Error', 'badInputs'=>['title']]);
$item = Item::whereId($id)->first();
if (!$item) return Response()->json(['message'=>'System Error', 'badInputs'=>['title']]);
if (!$user->ownsItem($id)) return Response()->json(['message'=>"You don't own this item!", 'badInputs'=>['title']]);
$inventory = Inventory::where('item_id', $id)->where('owner_id', $user->id)->first();
$data = $request->all();
$valid = Validator::make($data, [
'price' => ['required', 'integer', 'min:1'],
]);
if ($valid->stopOnFirstFailure()->fails()) {
$error = $valid->errors()->first();
$messages = $valid->messages()->get('*');
return Response()->json(['message'=>$error, 'badInputs'=>[array_keys($messages)]]);
}
$selling = new Selling;
$selling->seller_id = $user->id;
$selling->uid = $inventory->uid;
$selling->price = $request->input('price');
$item->sellingPrices()->save($selling);
$sellingItemNew = Selling::where('item_id', $id)->first();
if ($item->sellingPrices()->count() <= 0) {$item->current_price = null;$item->save();}else{$item->current_price = $sellingItemNew->price;$item->save();}
$replies = $item->sellingPrices()->orderBy('price', 'asc')->paginate(10);
$itemA = $item->toArray();
$itemA['creator'] = User::where('id', $item->creator_id)->first();
foreach ($replies as &$reply) {
$creator = User::where('id', $reply['seller_id'])->first();
$reply['created_at'] = explode('T', $reply['created_at'])[0];
$reply['seller_name'] = $creator->username;
}
return Response()->json(['message'=>"Success!", 'badInputs'=>[], "item"=>$itemA, "sellingPrices"=>$replies]);
}
}

View File

@ -18,6 +18,7 @@ use App\Models\Friend;
use App\Models\Feed;
use App\Models\Item;
use App\Models\Inventory;
use App\Models\Selling;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
@ -160,4 +161,45 @@ class Controller extends BaseController
return Response()->json(["post"=>$postA,"replies"=>$replies]);
}
public function fetchItem(Request $request, $id) {
$user = AuthHelper::GetCurrentUser($request);
$item = Item::where('id', $id)->first();
if (!$item) return Response()->json(false);
$itemA = $item->toArray();
$realDate = explode('T', $itemA['created_at'])[0];
$itemA['created_at'] = $realDate;
$itemA['creator'] = User::where('id', $item->creator_id)->first();
if ($user) {
$sellingItem = Selling::where('seller_id', $user->id)->first();
if ($sellingItem) {
$itemA['isSelling'] = true;
} else {
$itemA['isSelling'] = false;
}
} else {
$itemA['isSelling'] = false;
}
if ($user && $user->ownsItem($id)) {$itemA['ownsItem'] = true;}else{$itemA['ownsItem'] = false;}
$replies = $item->sellingPrices()->orderBy('price', 'asc')->paginate(10);
foreach ($replies as &$reply) {
$creator = User::where('id', $reply['seller_id'])->first();
$reply['created_at'] = explode('T', $reply['created_at'])[0];
$reply['seller_name'] = $creator->username;
}
return Response()->json(["item"=>$itemA,"sellingPrices"=>$replies]);
}
}

View File

@ -41,29 +41,6 @@ class HomeController extends Controller
return view('home');
}
public function settingsAbout(Request $request) {
$data = $request->all();
$valid = Validator::make($data, [
'body' => ['required', 'string', 'min:2', 'max:180'],
]);
if ($valid->stopOnFirstFailure()->fails()) {
$error = $valid->errors()->first();
$messages = $valid->messages()->get('*');
return Response()->json(['message'=>$error, 'badInputs'=>[array_keys($messages)]]);
}
$user = AuthHelper::GetCurrentUser($request);
$user->about = $_POST['body'];
$user->save();
return Response()->json(['message'=>'Success!', 'badInputs'=>[]]);
}
public function createPost(Request $request) {
$data = $request->all();
@ -206,7 +183,7 @@ class HomeController extends Controller
}
public function createReply($id) {
public function createReply(Request $request, $id) {
$data = $request->all();

View File

@ -0,0 +1,130 @@
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use App\Models\User\UserSession;
use App\Models\Post;
use App\Models\Reply;
use App\Models\Category;
use App\Models\Friend;
use App\Models\Feed;
use App\Models\Staff;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use App\Helpers\AuthHelper;
use Illuminate\Http\Request;
use Auth;
class SettingsController extends Controller
{
/**
* Create a new controller instance.
*
* @return void
*/
/*public function __construct()
{
$this->middleware('auth');
}*/
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
return view('home');
}
public function settingsAbout(Request $request) {
$data = $request->all();
$valid = Validator::make($data, [
'body' => ['required', 'string', 'min:2', 'max:180'],
]);
if ($valid->stopOnFirstFailure()->fails()) {
$error = $valid->errors()->first();
$messages = $valid->messages()->get('*');
return Response()->json(['message'=>$error, 'badInputs'=>[array_keys($messages)]]);
}
$user = AuthHelper::GetCurrentUser($request);
$user->about = $_POST['body'];
$user->save();
return Response()->json(['message'=>'Success!', 'badInputs'=>[]]);
}
public function settingsPassword(Request $request) {
$data = $request->all();
$valid = Validator::make($data, [
'currentPassword' => ['required', 'string'],
'newPassword' => ['required', 'string', 'min:8'],
'checkNewPassword' => ['required', 'string'],
]);
if ($valid->stopOnFirstFailure()->fails()) {
$error = $valid->errors()->first();
$messages = $valid->messages()->get('*');
return Response()->json(['message'=>$error, 'badInputs'=>[array_keys($messages)]]);
}
$user = AuthHelper::GetCurrentUser($request);
if (!$user) return Response()->json(['message'=>'User not found!', 'badInputs'=>['title']]);
if (!Hash::check($request->input('currentPassword'), $user->password))
return Response()->json(['message'=>'Thats not the right password!', 'badInputs'=>['currentPassword']]);
if ($request->input('newPassword') != $request->input('checkNewPassword'))
return Response()->json(['message'=>'Those dont match!', 'badInputs'=>['checkNewPassword', 'newPassword']]);
$user->password = Hash::make($request->input('newPassword'));
$user->save();
return Response()->json(['message'=>'Success!', 'badInputs'=>[]]);
}
public function settingsEmail(Request $request) {
$data = $request->all();
$valid = Validator::make($data, [
'currentPassword' => ['required', 'string'],
'newEmail' => ['required', 'string', 'email', 'max:255'],
]);
if ($valid->stopOnFirstFailure()->fails()) {
$error = $valid->errors()->first();
$messages = $valid->messages()->get('*');
return Response()->json(['message'=>$error, 'badInputs'=>[array_keys($messages)]]);
}
$user = AuthHelper::GetCurrentUser($request);
if (!$user) return Response()->json(['message'=>'User not found!', 'badInputs'=>['title']]);
if (!Hash::check($request->input('currentPassword'), $user->password))
return Response()->json(['message'=>'Thats not the right password!', 'badInputs'=>['currentPassword']]);
$user->email = $request->input('newEmail');
$user->email_verified_at = null;
$user->save();
return Response()->json(['message'=>'Success!', 'badInputs'=>[]]);
}
}

View File

@ -12,4 +12,14 @@ class Item extends Model
protected $table = 'items';
function sellingPrices()
{
return $this->morphMany('App\Models\Selling', 'item');
}
function prices()
{
return $this->morphMany('App\Models\Price', 'item');
}
}

15
web/app/Models/Prices.php Normal file
View File

@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Prices extends Model
{
use HasFactory;
protected $table = 'prices';
}

View File

@ -0,0 +1,14 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Selling extends Model
{
use HasFactory;
protected $table = 'sellings';
}

View File

@ -23,6 +23,17 @@ class User extends Model
'email_verified_at'
];
function inventory()
{
return $this->morphMany('App\Models\Inventory', 'owner');
}
public function ownsItem($id)
{
$ownedItem = Inventory::where('item_id', $id)->where('owner_id', $this->id)->first();
if ($ownedItem) {return true;}else{return false;}
}
public function getFriends($decision, $pending, $id) {
switch($decision) {

View File

@ -20,9 +20,11 @@ class CreateItemsTable extends Migration
$table->string('thumbnail');
$table->integer('creator_id');
$table->integer('starting_price')->default(5);
$table->integer('current_price')->default(5);
$table->integer('current_price')->default(5)->nullable();
$table->integer('category_id')->default(1);
$table->string('category_type');
$table->integer('isLimited')->default(0);
$table->integer('stock')->nullable(); //default is null because most items won't be limiteds.
//may need to add more later idk.
$table->timestamps();
});

View File

@ -18,7 +18,7 @@ class CreateInventoriesTable extends Migration
$table->string('item_id');
$table->string('owner_id');
$table->string('owner_type');
$table->string('uid'); //unique id | used for limiteds, the original id of the inventory row. once set, it never changes
$table->string('uid')->nullable(); //unique id | used for limiteds, the original id of the inventory row. once set, it never changes
$table->boolean('status')->default(true);
$table->timestamps();
});

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateSellingsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('sellings', function (Blueprint $table) {
$table->id();
$table->integer('seller_id');
$table->integer('item_id');
$table->string('item_type');
$table->integer('uid')->nullable(); //unique id | used for limiteds, the original id of the inventory row. once set, it never changes
$table->integer('price');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('sellings');
}
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePricesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('prices', function (Blueprint $table) {
$table->id();
$table->string('item_id');
$table->string('item_type');
$table->string('price');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('prices');
}
}

11335
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,7 @@
"@babel/preset-react": "^7.13.13",
"@popperjs/core": "^2.9.2",
"axios": "^0.21",
"bootstrap": "5.0.1",
"bootstrap": "^5.1.3",
"css-loader": "^6.4.0",
"jquery": "^3.6.0",
"laravel-mix": "^6.0.6",
@ -38,7 +38,7 @@
"@steveeeie/react-page-transition": "^1.2.1",
"laravel-mix-react-css-modules": "^2.0.0",
"mini-css-extract-plugin": "^2.4.3",
"react-bootstrap": "^2.0.2",
"react-bootstrap": "^2.2.2",
"react-google-recaptcha": "^2.1.0",
"react-router-guards": "^1.0.2",
"styled-components": "^5.3.1",

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -16,6 +16,18 @@ export const Card = (props) => {
);
};
export const BigCard = (props) => {
return (
<div className="container graphictoria-center-vh">
<div className={`card shadow-sm ${props.className}`}>
<div className={`card-body ${props.padding? `p5r` : null} text-center`}>
{ props.children }
</div>
</div>
</div>
);
};
export const CardTitle = (props) => {
return (
<>

View File

@ -36,6 +36,7 @@ import CreateReply from '../pages/CreateReply.js';
import Settings from '../pages/Settings.js';
import User from '../pages/User.js';
import Catalog from '../pages/Catalog.js';
import Item from '../pages/Item.js';
axios.defaults.withCredentials = true
@ -170,6 +171,10 @@ const App = () => {
<Catalog user={user}/>
</Route>
<GuardedRoute exact path="/item/:id" meta={{auth: true}}>
<Item user={user}/>
</GuardedRoute>
<Route exact path="/catalog/category/:id">
<Catalog user={user}/>
</Route>

View File

@ -105,12 +105,12 @@ const Catalog = (props) => {
<>
<Link to={`/item/${item.id}`} className={`flex graphic-post-column col-3`}>
<div className={`flex column mb-10 alc`}>
[Thumbnail.]
<img src='/images/testing/hat.png' className='img-fluid graphic-thumb' />
</div>
<div className={`flex flex-column m-0`}>
<div className={`flex flex-column m-0 jcc alc`}>
<div className={`flex row w-fit-content`}><h6 className={`m-0 mr-15 fs13`}>{item.title}</h6></div>
<div className={`row fs15 w-fit-content`}>
<p className={`w-fit-content`}>${item.current_price}</p>
<p className={`w-fit-content`}>{item.current_price? `$${item.current_price}` : `N/A`}</p>
</div>
</div>
</Link>

View File

@ -40,7 +40,7 @@ const CreateReply = (props) => {
form.append('creator_id', user.id);
form.append('token', encodeURIComponent(getCookie(`gtok`)));
setWaitingForSubmission(true);
await axios.post(`${protocol}apis.${url}/api/create/reply/${post.post.id}`, form, {headers: {'X-CSRF-TOKEN': document.querySelector(`meta[name="csrf-token"]`).content, "X-Requested-With":"XMLHttpRequest"}}).then(data=>{
await axios.post(`${protocol}apis.${url}/api/create/reply/${postId}`, form, {headers: {'X-CSRF-TOKEN': document.querySelector(`meta[name="csrf-token"]`).content, "X-Requested-With":"XMLHttpRequest"}}).then(data=>{
const res = data.data;
console.log(res);
if (res.badInputs.length >= 1) {
@ -54,7 +54,7 @@ const CreateReply = (props) => {
}
return (
waitingForSubmission && !post.loading? <Loader/> :
waitingForSubmission || post.loading? <Loader/> :
<Card>
<CardTitle>Reply to '{post.post.title}'</CardTitle>
<div className="p-2 row">

View File

@ -105,7 +105,7 @@ const Forum = (props) => {
<>
<Link to={`/forum/post/${post.id}`} className={`flex graphic-post`}>
<div className={`flex column mr-15 alc`}>
[Avatar.]
<img src='/images/testing/headshot.png' className='img-fluid graphic-thumb' />
</div>
<div className={`flex row m-0`}>
<div className={`flex row`}><h5 className={`m-0 mr-15`}>{post.locked == 1? <i class="fa-solid fa-lock"></i> : null} {post.title}</h5></div>

198
web/resources/js/pages/Item.js vendored Normal file
View File

@ -0,0 +1,198 @@
// © 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 '../Components/Card.js';
import { paginate } from '../helpers/utils.js';
import { Modal } from 'react-bootstrap';
var url = Config.BaseUrl.replace('http://', '');
var protocol = Config.Protocol;
const Item = (props) => {
var id = useParams().id;
const [validity, setValidity] = useState({error: false, message: ``, inputs: []});
const [state, setState] = useState({offline: false, loading: true});
const [item, setPost] = useState({item: [], sellingPrices: {sellingPrices: [], meta: [], currentPage: 1}});
const [show, setShow] = useState(false);
const user = props.user;
const history = useHistory();
const fetchItem = async () => {
await axios.get(`${protocol}apis.${url}/fetch/item/${id}?page=${item.sellingPrices.currentPage}`, {headers: {"X-Requested-With":"XMLHttpRequest"}}).then(data=>{
if (!data.data) {history.push(`/catalog`);}
const res = data.data;
SetTitle(res.item.title);
setPost({item: res.item, sellingPrices: {...item.sellingPrices, sellingPrices: res.sellingPrices.data, meta: res.sellingPrices}});
}).catch(error=>{console.log(error);});
}
const buyItem = async (decision, isSelling, sellingId) => {
setState({...state, loading: true});
const body = new FormData();
body.append(`decision`, decision);
if (isSelling) body.append('sellingId', sellingId);
await axios.post(`${protocol}apis.${url}/api/catalog/buy/${id}?page=${item.sellingPrices.currentPage}`, body, {headers: {"X-Requested-With":"XMLHttpRequest"}}).then(data=>{
if (!data.data) {history.push(`/catalog`);}
setState({...state, loading: false});
const res = data.data;
if (res.badInputs.length >= 1) {
setValidity({error: true, message:res.message, inputs: res.badInputs});
setTimeout(()=>{setValidity({...validity, error: false, inputs: res.badInputs});}, 4000);
}else{
setPost({item: res.item, sellingPrices: {...item.sellingPrices, sellingPrices: res.sellingPrices.data, meta: res.sellingPrices}});
}
}).catch(error=>{console.log(error);});
}
const sellItem = async (form) => {
setState({...state, loading: true});
const body = form;
await axios.post(`${protocol}apis.${url}/api/catalog/sell/${id}?page=${item.sellingPrices.currentPage}`, body, {headers: {"X-Requested-With":"XMLHttpRequest"}}).then(data=>{
if (!data.data) {history.push(`/catalog`);}
setState({...state, loading: false});
const res = data.data;
if (res.badInputs.length >= 1) {
setValidity({error: true, message:res.message, inputs: res.badInputs});
setTimeout(()=>{setValidity({...validity, error: false, inputs: res.badInputs});}, 4000);
}else{
setPost({item: res.item, sellingPrices: {...item.sellingPrices, sellingPrices: res.sellingPrices.data, meta: res.sellingPrices}});
}
}).catch(error=>{console.log(error);});
}
const paginatePrices = async (decision) => {
paginate(decision, item.sellingPrices.currentPage, item.sellingPrices.meta).then(res=>{
switch(res){
case "increase":
setPost({...item, sellingPrices: {...item.sellingPrices, currentPage: item.sellingPrices.currentPage+1}});
break;
case "decrease":
setPost({...item, sellingPrices: {...item.sellingPrices, currentPage: item.sellingPrices.currentPage-1}});
break;
default:
break;
}
}).catch(error=>console.log(error));
}
useEffect(async ()=>{
setState({...state, loading: false});
}, []);
useEffect(async()=>{
setState({...state, loading: true});
await fetchItem();
setState({...state, loading: false});
}, [item.sellingPrices.currentPage]);
return (
state.loading
?
<Loader />
:
<div className={`flex w-100 column jcc alc`}>
<Modal show={show} onHide={(e)=>{setShow(false);}}>
<Modal.Header closeButton>
<Modal.Title>Sell {item.item.title}</Modal.Title>
</Modal.Header>
<Modal.Body className={`flex flex-column`}>
<form onSubmit={(e)=>{e.preventDefault();sellItem(new FormData(e.target));}}>
<h4>How much are you selling for?</h4>
<input placeholder={`Ex. $500`} name={`price`} type={`number`} className={`mb-15`}/><br/>
<button className={`btn btn-success w-fit-content`} type={`submit`}>Sell Item</button>
</form>
</Modal.Body>
<Modal.Footer>
<button className={`btn btn-danger w-fit-content`} onClick={(e)=>{setShow(false);}}>Cancel</button>
</Modal.Footer>
</Modal>
{validity.error?
<div className={`px-5 mb-10 w-60 justify-content-center align-items-center`}>
<div className={`error-dialog w-100`}>
<p className={`mb-0`}>{validity.message}</p>
</div>
</div>
: null}
<Card>
<div className={`flex w-100 column`}>
<div className={`flex row fs12`}>
<div className={`row w-fit-content`}>
<p className={`w-fit-content`}>Date Created:</p>
<p className={`w-fit-content padding-none`}>{item.item.created_at}</p>
</div>
</div>
<hr/>
<div className={`flex row`}>
<div className={`col text-left flex flex-column`}>
<img src='/images/testing/hat.png' className='img-fluid graphic-thumb-big'/>
<p>Price: <strong>{item.item.current_price? `$${item.item.current_price}` : `N/A`}</strong></p>
<p>Description: <i>'{item.item.description}'</i></p>
{item.item.ownsItem? <strong><p>You own this item!</p></strong> : null}
{!item.item.isLimited? <button className={`btn btn-success w-fit-content`} onClick={(e)=>{buyItem(`nonLimited`, false, null);}}>Buy Item</button> : item.item.stock >= 1? <button className={`btn btn-success w-fit-content`} onClick={(e)=>{buyItem(`limited`, false, null);}}>Buy Limited</button> : !item.item.isSelling && item.item.ownsItem? <button className={`btn btn-danger w-fit-content`} onClick={(e)=>{show? setShow(false) : setShow(true)}}>Sell Item</button> : item.item.isSelling? <p>You're selling this item!</p> : <p>Sorry, you don't own this item!</p>}
</div>
<div className={`flex column col flex-column jcc alc`}>
<div className={`flex flex-column`}>
<h5 className={`text-left`}>Creator Info:</h5>
<img src='/images/testing/avatar.png' className='img-fluid graphic-thumb-big' />
<Link to={`/user/${item.item.creator.id}`}>{item.item.creator.username}</Link>
</div>
</div>
</div>
</div>
</Card>
{item.item.isLimited != 0 && item.item.stock <= 0? <div className={`container`}><hr className={`graphictoria-small-card mt-15 mb-15`}/></div> : null}
{item.sellingPrices.sellingPrices.length <= 0 && item.item.isLimited != 0 && item.item.stock <= 0? <p className={`w-100 text-center`}>No one is selling this item yet!</p> : null}
{item.item.isLimited != 0 && item.item.stock <= 0?
<div className={`flex column w-100`}>
{item.sellingPrices.sellingPrices.map(reply=>(
<div className={`mb-15`}>
<Card>
<div className={`flex w-100 column`}>
<div className={`flex row fs12`}>
<div className={`row w-fit-content`}>
<p className={`w-fit-content`}>Date posted:</p>
<p className={`w-fit-content padding-none`}>{reply.created_at}</p>
</div>
</div>
<hr/>
<div className={`flex row`}>
<div className={`flex column jcc alc col-3`}>
<img src='/images/testing/headshot.png' className='img-fluid graphic-thumb' />
<Link to={`/user/${reply.seller_id}`}>{reply.seller_name}</Link>
</div>
<div className={`col text-left float-right flex alc jcc`}>
<div className={`flex flex-column flex alc jcc`}>
<p>Price: ${reply.price}</p>
<p>Item UID: {reply.uid}</p>
<button className={`btn btn-success w-fit-content`} onClick={(e)=>{buyItem(`selling`, true, reply.id);}}>Buy</button>
</div>
</div>
</div>
</div>
</Card>
</div>
))}
{item.sellingPrices.sellingPrices.length >= 10?
<div className={`w-100 jcc alc row mt-15`}>
{item.sellingPrices.currentPage >= 2? <button className={`w-fit-content btn btn-primary mr-15`} onClick={(e)=>{paginatePrices(true);}}>Previous Page</button> : null}
{item.sellingPrices.currentPage < item.sellingPrices.meta.last_page? <button className={`w-fit-content btn btn-primary`} onClick={(e)=>{paginatePrices(false);}}>Next Page</button> : null}
</div> : null}
</div> : null}
</div>
);
}
export default Item;

View File

@ -93,7 +93,7 @@ const Post = (props) => {
<hr/>
<div className={`flex row`}>
<div className={`flex column jcc alc col-3`}>
<p className={`mb-10`}>[Avatar.]</p>
<img src='/images/testing/headshot.png' className='img-fluid graphic-thumb' />
<Link to={`/user/${post.post.creator.id}`}>{post.post.creator.username}</Link>
</div>
@ -119,7 +119,7 @@ const Post = (props) => {
<hr/>
<div className={`flex row`}>
<div className={`flex column jcc alc col-3`}>
<p className={`mb-10`}>[Avatar.]</p>
<img src='/images/testing/headshot.png' className='img-fluid graphic-thumb' />
<Link to={`/user/${reply.creator_id}`}>{reply.creator_name}</Link>
</div>
<div className={`col text-left`}>

View File

@ -12,7 +12,7 @@ import SetTitle from "../Helpers/Title.js";
import Loader from '../Components/Loader.js';
import { GenericErrorModal } from './Errors.js';
import { Card, CardTitle } from '../Components/Card.js';
import { BigCard, Card, CardTitle } from '../Components/Card.js';
import { getCookie, paginate } from '../helpers/utils.js';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@ -31,7 +31,7 @@ const Settings = (props) => {
switch(setting){
case "aboutMe":
setState({...state, loading: true});
const body = form;
var body = form;
body.append('token', encodeURIComponent(getCookie(`gtok`)));
axios.post(`${protocol}apis.${url}/api/change/user/about`, body, {headers: {'X-CSRF-TOKEN': document.querySelector(`meta[name="csrf-token"]`).content, "X-Requested-With":"XMLHttpRequest"}}).then(data=>{
const res = data.data;
@ -39,7 +39,37 @@ const Settings = (props) => {
setValidity({error: true, message:res.message, inputs: res.badInputs});
setTimeout(()=>{setValidity({...validity, error: false, inputs: res.badInputs});}, 4000);
}else{
history.push(`/user/${user.id}`);
window.location.replace(`/user/${user.id}`); //updates entire user
}
setState({...state, loading: false});
}).catch(error=>{console.log(error);});
break;
case "changePassword":
setState({...state, loading: true});
var body = form;
body.append('token', encodeURIComponent(getCookie(`gtok`)));
axios.post(`${protocol}apis.${url}/api/change/user/password`, 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) {
setValidity({error: true, message:res.message, inputs: res.badInputs});
setTimeout(()=>{setValidity({...validity, error: false, inputs: res.badInputs});}, 4000);
}else{
window.location.replace(`/home`); //updates entire user
}
setState({...state, loading: false});
}).catch(error=>{console.log(error);});
break;
case "changeEmail":
setState({...state, loading: true});
var body = form;
body.append('token', encodeURIComponent(getCookie(`gtok`)));
axios.post(`${protocol}apis.${url}/api/change/user/email`, 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) {
setValidity({error: true, message:res.message, inputs: res.badInputs});
setTimeout(()=>{setValidity({...validity, error: false, inputs: res.badInputs});}, 4000);
}else{
window.location.replace(`/home`); //updates entire user
}
setState({...state, loading: false});
}).catch(error=>{console.log(error);});
@ -88,11 +118,51 @@ const Settings = (props) => {
</div>
<div className="graphictoria-nav-splitter"></div>
<div className={`col flex row`}>
<div className={`col flex row jcc alc`}>
<p>Another Settings.</p>
<div className={`col flex jcc alc`}>
<BigCard className={`w-100`}>
<CardTitle>Change your Password</CardTitle>
<div>
<form method={`OPTIONS`} onSubmit={(e)=>{e.preventDefault();changeSettings(`changePassword`, new FormData(e.target));}} className={`flex column jcc alc`}>
<div className={`flex flex-column w-100 mb-15`}>
<p>Current Password</p>
<input placeholder={`Your Current Password`} className={`w-100 ${(validity.inputs.find(input=>input == `body`)? `is-invalid` : null)}`} name={`currentPassword`}/>
</div>
<div className={`flex flex-row w-100`}>
<div className={`flex flex-column w-100 mr-15`}>
<p>New Password</p>
<input placeholder={`New Password`} className={`w-100 ${(validity.inputs.find(input=>input == `body`)? `is-invalid` : null)}`} name={`newPassword`}/>
</div>
<div className={`flex flex-column w-100`}>
<p>Re-enter New Password</p>
<input placeholder={`Re-enter new Password`} className={`w-100 ${(validity.inputs.find(input=>input == `body`)? `is-invalid` : null)}`} name={`checkNewPassword`}/>
</div>
</div>
<button className={`btn btn-success mt-15`}>Submit</button>
</form>
</div>
</BigCard>
</div>
<div className={`col flex row jcc alc`}>
<p>Another Settings.</p>
<div className={`col flex jcc alc`}>
<BigCard className={`w-100`}>
<CardTitle>Change your Email</CardTitle>
<div>
<form method={`OPTIONS`} onSubmit={(e)=>{e.preventDefault();changeSettings(`changeEmail`, new FormData(e.target));}} className={`flex column jcc alc`}>
<div className={`flex flex-row w-100`}>
<div className={`flex flex-column w-100 mr-15`}>
<p>New Email</p>
<input placeholder={`New Email`} className={`w-100 ${(validity.inputs.find(input=>input == `body`)? `is-invalid` : null)}`} name={`newEmail`}/>
</div>
<div className={`flex flex-column w-100`}>
<p>Current Password</p>
<input placeholder={`Current Password`} className={`w-100 ${(validity.inputs.find(input=>input == `body`)? `is-invalid` : null)}`} name={`currentPassword`}/>
</div>
</div>
<button className={`btn btn-success mt-15`}>Submit</button>
</form>
</div>
</BigCard>
</div>
</div>
</div>
</div>

View File

@ -86,7 +86,7 @@ const User = (props) => {
<div className={`flex flex-column justify-content-center align-items-center`}>
{
!metaUser? null :
userId == metaUser.id? <p>This is you!</p> :
userId == metaUser.id? <Link className={`btn btn-primary w-fit-content`} to={`/auth/settings`}>Settings</Link> :
isFriend && isFriend == `pending`?
<button className={`btn btn-dark disabled w-fit-content`}>Pending...</button>
: isFriend && isFriend == `needToAccept`?

View File

@ -55,6 +55,24 @@ $web-font-path: "https://fonts.googleapis.com/css2?family=Source+Sans+Pro:ital,w
text-decoration: none !important;
}
.graphic-thumb-big {
height: 250px;
width: 250px;
object-fit: cover;
}
.modal-content {
background-color: #222 !important;
}
.float-right {
margin-left: auto !important;
}
.float-left {
margin-right: auto !important;
}
// Loader
.gtoria-loader-center {

View File

@ -58,13 +58,23 @@ Route::get('/fetch/posts/{id}', 'Controller@fetchPosts');
Route::get('/fetch/post/{id}', 'Controller@fetchPost');
Route::get('/fetch/item/{id}', 'Controller@fetchItem');
Route::post('/api/add/user/{id}', 'HomeController@addFriend');
Route::post('/api/create/forum', 'HomeController@createPost');
Route::post('/api/create/reply/{id}', 'HomeController@createReply');
Route::post('/api/change/user/about', 'HomeController@settingsAbout');
Route::post('/api/catalog/buy/{id}', 'CatalogController@buy');
Route::post('/api/catalog/sell/{id}', 'CatalogController@sell');
Route::post('/api/change/user/about', 'SettingsController@settingsAbout');
Route::post('/api/change/user/password', 'SettingsController@settingsPassword');
Route::post('/api/change/user/email', 'SettingsController@settingsEmail');
Route::fallback(function(){
return response('{"errors":[{"code":404,"message":"NotFound"}]}', 404)