mirror of
https://github.com/RoboSats/robosats.git
synced 2024-12-13 19:06:26 +00:00
Merge pull request #19 from Reckless-Satoshi/order-book-first-iteration
Add book order API endpoint and basic frontend cards page
This commit is contained in:
commit
7e89f57779
35
api/admin.py
35
api/admin.py
@ -1,3 +1,36 @@
|
||||
from django.contrib import admin
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from .models import Order, Profile
|
||||
|
||||
# Register your models here.
|
||||
admin.site.unregister(Group)
|
||||
admin.site.unregister(User)
|
||||
|
||||
class ProfileInline(admin.StackedInline):
|
||||
model = Profile
|
||||
can_delete = False
|
||||
fields = ('avatar_tag',)
|
||||
readonly_fields = ['avatar_tag']
|
||||
|
||||
# extended users with avatars
|
||||
@admin.register(User)
|
||||
class EUserAdmin(UserAdmin):
|
||||
inlines = [ProfileInline]
|
||||
list_display = ('avatar_tag',) + UserAdmin.list_display
|
||||
list_display_links = ['username']
|
||||
def avatar_tag(self, obj):
|
||||
return obj.profile.avatar_tag()
|
||||
|
||||
@admin.register(Order)
|
||||
class OrderAdmin(admin.ModelAdmin):
|
||||
list_display = ('id','type','maker','taker','status','amount','currency','created_at','expires_at')
|
||||
list_display_links = ('maker','taker')
|
||||
pass
|
||||
|
||||
@admin.register(Profile)
|
||||
class UserProfileAdmin(admin.ModelAdmin):
|
||||
list_display = ('avatar_tag','user','id','total_ratings','avg_rating','num_disputes','lost_disputes')
|
||||
list_display_links =['user']
|
||||
readonly_fields = ['avatar_tag']
|
||||
pass
|
@ -1,11 +1,16 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||
from django.core.validators import MaxValueValidator, MinValueValidator, validate_comma_separated_integer_list
|
||||
from django.db.models.signals import post_save, pre_delete
|
||||
from django.dispatch import receiver
|
||||
|
||||
from django.utils.html import mark_safe
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
#############################
|
||||
# TODO
|
||||
# Load hparams from .env file
|
||||
|
||||
min_satoshis_trade = 10*1000
|
||||
max_satoshis_trade = 500*1000
|
||||
|
||||
@ -45,6 +50,7 @@ class Order(models.Model):
|
||||
# order info, id = models.CharField(max_length=64, unique=True, null=False)
|
||||
status = models.PositiveSmallIntegerField(choices=Status.choices, default=Status.WFB)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
expires_at = models.DateTimeField()
|
||||
|
||||
# order details
|
||||
type = models.PositiveSmallIntegerField(choices=Types.choices, null=False)
|
||||
@ -56,7 +62,7 @@ class Order(models.Model):
|
||||
is_explicit = models.BooleanField(default=False, null=False) # pricing method. A explicit amount of sats, or a relative premium above/below market.
|
||||
|
||||
# order participants
|
||||
maker = models.ForeignKey(User, related_name='maker', on_delete=models.SET_NULL, null=True, default=None) # unique = True, a maker can only make one order
|
||||
maker = models.ForeignKey(User, related_name='maker', on_delete=models.CASCADE, null=True, default=None) # unique = True, a maker can only make one order
|
||||
taker = models.ForeignKey(User, related_name='taker', on_delete=models.SET_NULL, null=True, default=None) # unique = True, a taker can only take one order
|
||||
|
||||
# order collateral
|
||||
@ -71,3 +77,40 @@ class Order(models.Model):
|
||||
# buyer payment LN invoice
|
||||
has_invoice = models.BooleanField(default=False, null=False) # has invoice and is valid
|
||||
invoice = models.CharField(max_length=300, unique=False, null=True, default=None)
|
||||
|
||||
class Profile(models.Model):
|
||||
user = models.OneToOneField(User,on_delete=models.CASCADE)
|
||||
|
||||
# Ratings stored as a comma separated integer list
|
||||
total_ratings = models.PositiveIntegerField(null=False, default=0)
|
||||
latest_ratings = models.CharField(max_length=999, null=True, default=None, validators=[validate_comma_separated_integer_list]) # Will only store latest ratings
|
||||
avg_rating = models.DecimalField(max_digits=4, decimal_places=1, default=None, null=True, validators=[MinValueValidator(0), MaxValueValidator(100)])
|
||||
|
||||
# Disputes
|
||||
num_disputes = models.PositiveIntegerField(null=False, default=0)
|
||||
lost_disputes = models.PositiveIntegerField(null=False, default=0)
|
||||
|
||||
# RoboHash
|
||||
avatar = models.ImageField(default="static/assets/avatars/unknown.png", verbose_name='Avatar')
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def create_user_profile(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
Profile.objects.create(user=instance)
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def save_user_profile(sender, instance, **kwargs):
|
||||
instance.profile.save()
|
||||
|
||||
def __str__(self):
|
||||
return self.user.username
|
||||
|
||||
# to display avatars in admin panel
|
||||
def get_avatar(self):
|
||||
if not self.avatar:
|
||||
return 'static/assets/avatars/unknown.png'
|
||||
return self.avatar.url
|
||||
|
||||
# method to create a fake table field in read only mode
|
||||
def avatar_tag(self):
|
||||
return mark_safe('<img src="%s" width="50" height="50" />' % self.get_avatar())
|
||||
|
@ -4,7 +4,7 @@ from .models import Order
|
||||
class OrderSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = ('id','status','created_at','type','currency','amount','payment_method','is_explicit','premium','satoshis','maker','taker')
|
||||
fields = ('id','status','created_at','expires_at','type','currency','amount','payment_method','is_explicit','premium','satoshis','maker','taker')
|
||||
|
||||
class MakeOrderSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
|
@ -1,8 +1,9 @@
|
||||
from django.urls import path
|
||||
from .views import MakeOrder, OrderView, UserGenerator
|
||||
from .views import MakeOrder, OrderView, UserGenerator, BookView
|
||||
|
||||
urlpatterns = [
|
||||
path('make/', MakeOrder.as_view()),
|
||||
path('order/', OrderView.as_view()),
|
||||
path('usergen/', UserGenerator.as_view()),
|
||||
path('book/', BookView.as_view()),
|
||||
]
|
67
api/views.py
67
api/views.py
@ -3,6 +3,7 @@ from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf.urls.static import static
|
||||
|
||||
from .serializers import OrderSerializer, MakeOrderSerializer
|
||||
from .models import Order
|
||||
@ -17,6 +18,12 @@ from pathlib import Path
|
||||
from datetime import timedelta
|
||||
from django.utils import timezone
|
||||
|
||||
# .env
|
||||
expiration_time = 8
|
||||
|
||||
avatar_path = Path('frontend/static/assets/avatars')
|
||||
avatar_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create your views here.
|
||||
|
||||
class MakeOrder(APIView):
|
||||
@ -51,6 +58,7 @@ class MakeOrder(APIView):
|
||||
premium=premium,
|
||||
satoshis=satoshis,
|
||||
is_explicit=is_explicit,
|
||||
expires_at= timezone.now()+timedelta(hours=expiration_time),
|
||||
maker=request.user)
|
||||
order.save()
|
||||
|
||||
@ -81,7 +89,7 @@ class OrderView(APIView):
|
||||
|
||||
#To do fix: data['status_message'] = Order.Status.get(order.status).label
|
||||
data['status_message'] = Order.Status.WFB.label # Hardcoded WFB, should use order.status value.
|
||||
|
||||
|
||||
data['maker_nick'] = str(order.maker)
|
||||
data['taker_nick'] = str(order.taker)
|
||||
|
||||
@ -96,7 +104,6 @@ class OrderView(APIView):
|
||||
|
||||
return Response({'Bad Request':'Order ID parameter not found in request'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
|
||||
class UserGenerator(APIView):
|
||||
lookup_url_kwarg = 'token'
|
||||
NickGen = NickGenerator(
|
||||
@ -106,7 +113,7 @@ class UserGenerator(APIView):
|
||||
use_noun=True,
|
||||
max_num=999)
|
||||
|
||||
def get(self,request):
|
||||
def get(self,request, format=None):
|
||||
'''
|
||||
Get a new user derived from a high entropy token
|
||||
|
||||
@ -139,18 +146,20 @@ class UserGenerator(APIView):
|
||||
|
||||
# generate avatar
|
||||
rh = Robohash(hash)
|
||||
rh.assemble(roboset='set1') # bgset='any' for backgrounds ON
|
||||
rh.assemble(roboset='set1', bgset='any')# for backgrounds ON
|
||||
|
||||
avatars_path = Path('frontend/static/assets/avatars')
|
||||
avatars_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with open(avatars_path.joinpath(nickname+".png"), "wb") as f:
|
||||
rh.img.save(f, format="png")
|
||||
# Does not replace image if existing (avoid re-avatar in case of nick collusion)
|
||||
|
||||
# Create new credentials if nickname is new
|
||||
image_path = avatar_path.joinpath(nickname+".png")
|
||||
if not image_path.exists():
|
||||
with open(image_path, "wb") as f:
|
||||
rh.img.save(f, format="png")
|
||||
|
||||
# Create new credentials and logsin if nickname is new
|
||||
if len(User.objects.filter(username=nickname)) == 0:
|
||||
User.objects.create_user(username=nickname, password=token, is_staff=False)
|
||||
user = authenticate(request, username=nickname, password=token)
|
||||
user.profile.avatar = str(image_path)[9:] # removes frontend/ from url (ugly, to be fixed)
|
||||
login(request, user)
|
||||
return Response(context, status=status.HTTP_201_CREATED)
|
||||
|
||||
@ -159,17 +168,15 @@ class UserGenerator(APIView):
|
||||
if user is not None:
|
||||
login(request, user)
|
||||
# Sends the welcome back message, only if created +30 mins ago
|
||||
if request.user.date_joined < (timezone.now()-timedelta(minutes=1)):
|
||||
if request.user.date_joined < (timezone.now()-timedelta(minutes=30)):
|
||||
context['found'] = 'We found your Robosat. Welcome back!'
|
||||
return Response(context, status=status.HTTP_202_ACCEPTED)
|
||||
else:
|
||||
# It is unlikely (1/20 Billions) but maybe the nickname is taken
|
||||
# It is unlikely, but maybe the nickname is taken (1 in 20 Billion change)
|
||||
context['found'] = 'Bad luck, this nickname is taken'
|
||||
context['bad_request'] = 'Enter a different token'
|
||||
return Response(context, status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
|
||||
|
||||
def delete(self,request):
|
||||
user = User.objects.get(id = request.user.id)
|
||||
|
||||
@ -177,10 +184,40 @@ class UserGenerator(APIView):
|
||||
# However it might be a long time recovered user
|
||||
# Only delete if user live is < 5 minutes
|
||||
|
||||
# TODO check if user exists AND it is not a maker or taker!
|
||||
if user is not None:
|
||||
avatar_file = avatar_path.joinpath(str(request.user)+".png")
|
||||
avatar_file.unlink() # Unsafe if avatar does not exist.
|
||||
logout(request)
|
||||
user.delete()
|
||||
return Response(status=status.HTTP_301_MOVED_PERMANENTLY)
|
||||
|
||||
return Response({'user_deleted':'User deleted permanently'},status=status.HTTP_301_MOVED_PERMANENTLY)
|
||||
|
||||
return Response(status=status.HTTP_403_FORBIDDEN)
|
||||
|
||||
class BookView(APIView):
|
||||
serializer_class = OrderSerializer
|
||||
|
||||
def get(self,request, format=None):
|
||||
currency = request.GET.get('currency')
|
||||
type = request.GET.get('type')
|
||||
queryset = Order.objects.filter(currency=currency, type=type, status=0) # TODO status = 1 for orders that are Public
|
||||
if len(queryset)== 0:
|
||||
return Response({'not_found':'No orders found, be the first to make one'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
queryset = queryset.order_by('created_at')
|
||||
book_data = []
|
||||
for order in queryset:
|
||||
data = OrderSerializer(order).data
|
||||
user = User.objects.filter(id=data['maker'])
|
||||
if len(user) == 1:
|
||||
data['maker_nick'] = user[0].username
|
||||
# TODO avoid sending status and takers for book views
|
||||
#data.pop('status','taker')
|
||||
book_data.append(data)
|
||||
|
||||
return Response(book_data, status=status.HTTP_200_OK)
|
||||
|
||||
|
||||
|
||||
|
||||
|
313
frontend/package-lock.json
generated
313
frontend/package-lock.json
generated
@ -191,7 +191,6 @@
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz",
|
||||
"integrity": "sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.16.7"
|
||||
}
|
||||
@ -558,7 +557,6 @@
|
||||
"version": "7.16.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz",
|
||||
"integrity": "sha512-Esxmk7YjA8QysKeT3VhTXvF6y77f/a91SIs4pWb4H2eWGQkCKFgQaG6hdoEVZtGsrAcb2K5BW66XsOErD4WU3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.16.7"
|
||||
}
|
||||
@ -1149,11 +1147,132 @@
|
||||
"integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==",
|
||||
"dev": true
|
||||
},
|
||||
"@emotion/babel-plugin": {
|
||||
"version": "11.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.7.2.tgz",
|
||||
"integrity": "sha512-6mGSCWi9UzXut/ZAN6lGFu33wGR3SJisNl3c0tvlmb8XChH1b2SUvxvnOh7hvLpqyRdHHU9AiazV3Cwbk5SXKQ==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.12.13",
|
||||
"@babel/plugin-syntax-jsx": "^7.12.13",
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@emotion/hash": "^0.8.0",
|
||||
"@emotion/memoize": "^0.7.5",
|
||||
"@emotion/serialize": "^1.0.2",
|
||||
"babel-plugin-macros": "^2.6.1",
|
||||
"convert-source-map": "^1.5.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"find-root": "^1.1.0",
|
||||
"source-map": "^0.5.7",
|
||||
"stylis": "4.0.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@emotion/cache": {
|
||||
"version": "11.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz",
|
||||
"integrity": "sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==",
|
||||
"requires": {
|
||||
"@emotion/memoize": "^0.7.4",
|
||||
"@emotion/sheet": "^1.1.0",
|
||||
"@emotion/utils": "^1.0.0",
|
||||
"@emotion/weak-memoize": "^0.2.5",
|
||||
"stylis": "4.0.13"
|
||||
}
|
||||
},
|
||||
"@emotion/hash": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
|
||||
},
|
||||
"@emotion/is-prop-valid": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.1.tgz",
|
||||
"integrity": "sha512-bW1Tos67CZkOURLc0OalnfxtSXQJMrAMV0jZTVGJUPSOd4qgjF3+tTD5CwJM13PHA8cltGW1WGbbvV9NpvUZPw==",
|
||||
"requires": {
|
||||
"@emotion/memoize": "^0.7.4"
|
||||
}
|
||||
},
|
||||
"@emotion/memoize": {
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz",
|
||||
"integrity": "sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ=="
|
||||
},
|
||||
"@emotion/react": {
|
||||
"version": "11.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.7.1.tgz",
|
||||
"integrity": "sha512-DV2Xe3yhkF1yT4uAUoJcYL1AmrnO5SVsdfvu+fBuS7IbByDeTVx9+wFmvx9Idzv7/78+9Mgx2Hcmr7Fex3tIyw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@emotion/cache": "^11.7.1",
|
||||
"@emotion/serialize": "^1.0.2",
|
||||
"@emotion/sheet": "^1.1.0",
|
||||
"@emotion/utils": "^1.0.0",
|
||||
"@emotion/weak-memoize": "^0.2.5",
|
||||
"hoist-non-react-statics": "^3.3.1"
|
||||
}
|
||||
},
|
||||
"@emotion/serialize": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.0.2.tgz",
|
||||
"integrity": "sha512-95MgNJ9+/ajxU7QIAruiOAdYNjxZX7G2mhgrtDWswA21VviYIRP1R5QilZ/bDY42xiKsaktP4egJb3QdYQZi1A==",
|
||||
"requires": {
|
||||
"@emotion/hash": "^0.8.0",
|
||||
"@emotion/memoize": "^0.7.4",
|
||||
"@emotion/unitless": "^0.7.5",
|
||||
"@emotion/utils": "^1.0.0",
|
||||
"csstype": "^3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"csstype": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
|
||||
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@emotion/sheet": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz",
|
||||
"integrity": "sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g=="
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"version": "11.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.6.0.tgz",
|
||||
"integrity": "sha512-mxVtVyIOTmCAkFbwIp+nCjTXJNgcz4VWkOYQro87jE2QBTydnkiYusMrRGFtzuruiGK4dDaNORk4gH049iiQuw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@emotion/babel-plugin": "^11.3.0",
|
||||
"@emotion/is-prop-valid": "^1.1.1",
|
||||
"@emotion/serialize": "^1.0.2",
|
||||
"@emotion/utils": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"@emotion/unitless": {
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
||||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
||||
},
|
||||
"@emotion/utils": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz",
|
||||
"integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA=="
|
||||
},
|
||||
"@emotion/weak-memoize": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz",
|
||||
"integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA=="
|
||||
},
|
||||
"@material-ui/core": {
|
||||
"version": "4.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz",
|
||||
@ -1230,6 +1349,65 @@
|
||||
"react-is": "^16.8.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"@mui/private-theming": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.2.3.tgz",
|
||||
"integrity": "sha512-Lc1Cmu8lSsYZiXADi9PBb17Ho82ZbseHQujUFAcp6bCJ5x/d+87JYCIpCBMagPu/isRlFCwbziuXPmz7WOzJPQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@mui/utils": "^5.2.3",
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"@mui/styled-engine": {
|
||||
"version": "5.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.2.6.tgz",
|
||||
"integrity": "sha512-bqAhli8eGS6v2qxivy2/4K0Ag8o//jsu1G2G6QcieFiT6y7oIF/nd/6Tvw6OSm3roOTifVQWNKwkt1yFWhGS+w==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@emotion/cache": "^11.7.1",
|
||||
"prop-types": "^15.7.2"
|
||||
}
|
||||
},
|
||||
"@mui/system": {
|
||||
"version": "5.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@mui/system/-/system-5.2.6.tgz",
|
||||
"integrity": "sha512-PZ7bmpWOLikWgqn2zWv9/Xa7lxnRBOmfjoMH7c/IVYJs78W3971brXJ3xV9MEWWQcoqiYQeXzUJaNf4rFbKCBA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@mui/private-theming": "^5.2.3",
|
||||
"@mui/styled-engine": "^5.2.6",
|
||||
"@mui/types": "^7.1.0",
|
||||
"@mui/utils": "^5.2.3",
|
||||
"clsx": "^1.1.1",
|
||||
"csstype": "^3.0.10",
|
||||
"prop-types": "^15.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"csstype": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
|
||||
"integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mui/types": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.0.tgz",
|
||||
"integrity": "sha512-Hh7ALdq/GjfIwLvqH3XftuY3bcKhupktTm+S6qRIDGOtPtRuq2L21VWzOK4p7kblirK0XgGVH5BLwa6u8z/6QQ=="
|
||||
},
|
||||
"@mui/utils": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.2.3.tgz",
|
||||
"integrity": "sha512-sQujlajIS0zQKcGIS6tZR0L1R+ib26B6UtuEn+cZqwKHsPo3feuS+SkdscYBdcCdMbrZs4gj8WIJHl2z6tbSzQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.16.3",
|
||||
"@types/prop-types": "^15.7.4",
|
||||
"@types/react-is": "^16.7.1 || ^17.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^17.0.2"
|
||||
}
|
||||
},
|
||||
"@types/eslint": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.2.1.tgz",
|
||||
@ -1268,6 +1446,11 @@
|
||||
"integrity": "sha512-+XBAjfZmmivILUzO0HwBJoYkAyyySSLg5KCGBDFLomJo0sV6szvVLAf4ANZZ0pfWzgEds5KmGLG9D5hfEqOhaA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/parse-json": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
|
||||
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="
|
||||
},
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz",
|
||||
@ -1290,6 +1473,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/react-is": {
|
||||
"version": "17.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz",
|
||||
"integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==",
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-transition-group": {
|
||||
"version": "4.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
|
||||
@ -1554,6 +1745,16 @@
|
||||
"object.assign": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-macros": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz",
|
||||
"integrity": "sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.7.2",
|
||||
"cosmiconfig": "^6.0.0",
|
||||
"resolve": "^1.12.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-polyfill-corejs2": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.0.tgz",
|
||||
@ -1619,6 +1820,11 @@
|
||||
"get-intrinsic": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001294",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001294.tgz",
|
||||
@ -1707,7 +1913,6 @@
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
|
||||
"integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.1"
|
||||
},
|
||||
@ -1715,8 +1920,7 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1738,6 +1942,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"cosmiconfig": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
|
||||
"integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
|
||||
"requires": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.1.0",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.7.2"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@ -1824,6 +2040,14 @@
|
||||
"integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
|
||||
"dev": true
|
||||
},
|
||||
"error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||
"requires": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"es-module-lexer": {
|
||||
"version": "0.9.3",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
|
||||
@ -1932,6 +2156,11 @@
|
||||
"pkg-dir": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"find-root": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
|
||||
},
|
||||
"find-up": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||
@ -1945,8 +2174,7 @@
|
||||
"function-bind": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||
},
|
||||
"gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
@ -1992,7 +2220,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
|
||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1"
|
||||
}
|
||||
@ -2048,6 +2275,22 @@
|
||||
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
||||
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
|
||||
"requires": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"import-local": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.3.tgz",
|
||||
@ -2064,11 +2307,15 @@
|
||||
"integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
|
||||
"dev": true
|
||||
},
|
||||
"is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
|
||||
},
|
||||
"is-core-module": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz",
|
||||
"integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has": "^1.0.3"
|
||||
}
|
||||
@ -2137,6 +2384,11 @@
|
||||
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
|
||||
"dev": true
|
||||
},
|
||||
"json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
@ -2243,6 +2495,11 @@
|
||||
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
|
||||
"dev": true
|
||||
},
|
||||
"lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||
},
|
||||
"loader-runner": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz",
|
||||
@ -2435,6 +2692,25 @@
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true
|
||||
},
|
||||
"parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"requires": {
|
||||
"callsites": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
"json-parse-even-better-errors": "^2.3.0",
|
||||
"lines-and-columns": "^1.1.6"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@ -2450,8 +2726,7 @@
|
||||
"path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "1.8.0",
|
||||
@ -2461,6 +2736,11 @@
|
||||
"isarray": "0.0.1"
|
||||
}
|
||||
},
|
||||
"path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="
|
||||
},
|
||||
"picocolors": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||
@ -2667,7 +2947,6 @@
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
|
||||
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.2.0",
|
||||
"path-parse": "^1.0.6"
|
||||
@ -2787,6 +3066,11 @@
|
||||
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
|
||||
"dev": true
|
||||
},
|
||||
"stylis": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
|
||||
"integrity": "sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
@ -2991,6 +3275,11 @@
|
||||
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
|
||||
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
|
||||
"dev": true
|
||||
},
|
||||
"yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-class-properties": "^7.16.7",
|
||||
"@emotion/react": "^11.7.1",
|
||||
"@emotion/styled": "^11.6.0",
|
||||
"@material-ui/core": "^4.12.3",
|
||||
"@material-ui/icons": "^4.11.2",
|
||||
"@mui/system": "^5.2.6",
|
||||
"material-ui-image": "^3.3.2",
|
||||
"react-router-dom": "^5.2.0"
|
||||
}
|
||||
|
@ -1,11 +1,173 @@
|
||||
import React, { Component } from "react";
|
||||
import { Paper, Button , Divider, Card, CardActionArea, CardContent, Typography, Grid, Select, MenuItem, FormControl, FormHelperText, List, ListItem, ListItemText, Avatar, Link, RouterLink, ListItemAvatar} from "@material-ui/core"
|
||||
|
||||
export default class BookPage extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
orders: new Array(),
|
||||
currency: 1,
|
||||
type: 1,
|
||||
};
|
||||
this.getOrderDetails()
|
||||
this.state.currencyCode = this.getCurrencyCode(this.state.currency)
|
||||
}
|
||||
|
||||
render() {
|
||||
return <p>This is the order book page</p>;
|
||||
}
|
||||
// Fix needed to handle HTTP 404 error when no order is found
|
||||
// Show message to be the first one to make an order
|
||||
getOrderDetails() {
|
||||
fetch('/api/book' + '?currency=' + this.state.currency + "&type=" + this.state.type)
|
||||
.then((response) => response.json())
|
||||
.then((data) => //console.log(data));
|
||||
this.setState({orders: data}));
|
||||
}
|
||||
|
||||
handleCardClick=(e)=>{
|
||||
console.log(e.target)
|
||||
this.props.history.push('/order/' + e.target);
|
||||
}
|
||||
|
||||
// Make these two functions sequential. getOrderDetails needs setState to be finish beforehand.
|
||||
handleTypeChange=(e)=>{
|
||||
this.setState({
|
||||
type: e.target.value,
|
||||
});
|
||||
this.getOrderDetails();
|
||||
}
|
||||
handleCurrencyChange=(e)=>{
|
||||
this.setState({
|
||||
currency: e.target.value,
|
||||
currencyCode: this.getCurrencyCode(e.target.value),
|
||||
})
|
||||
this.getOrderDetails();
|
||||
}
|
||||
|
||||
// Gets currency code (3 letters) from numeric (e.g., 1 -> USD)
|
||||
// Improve this function so currencies are read from json
|
||||
getCurrencyCode(val){
|
||||
return (val == 1 ) ? "USD": ((val == 2 ) ? "EUR":"ETH")
|
||||
}
|
||||
|
||||
// pretty numbers
|
||||
pn(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Grid className='orderBook' container spacing={1}>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h4" variant="h4">
|
||||
Order Book
|
||||
</Typography>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={6} align="right">
|
||||
<FormControl >
|
||||
<FormHelperText>
|
||||
I want to
|
||||
</FormHelperText>
|
||||
<Select
|
||||
label="Select Order Type"
|
||||
required="true"
|
||||
value={this.state.type}
|
||||
inputProps={{
|
||||
style: {textAlign:"center"}
|
||||
}}
|
||||
onChange={this.handleTypeChange}
|
||||
>
|
||||
<MenuItem value={1}>BUY</MenuItem>
|
||||
<MenuItem value={0}>SELL</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={6} align="left">
|
||||
<FormControl >
|
||||
<FormHelperText>
|
||||
And pay with
|
||||
</FormHelperText>
|
||||
<Select
|
||||
label="Select Payment Currency"
|
||||
required="true"
|
||||
value={this.state.currency}
|
||||
inputProps={{
|
||||
style: {textAlign:"center"}
|
||||
}}
|
||||
onChange={this.handleCurrencyChange}
|
||||
>
|
||||
<MenuItem value={1}>USD</MenuItem>
|
||||
<MenuItem value={2}>EUR</MenuItem>
|
||||
<MenuItem value={3}>ETH</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
{this.state.orders.map((order) =>
|
||||
<Grid container item sm={4}>
|
||||
<Card elevation={6} sx={{ width: 945 }}>
|
||||
|
||||
{/* To fix! does not pass order.id to handleCardCLick. Instead passes the clicked </>*/}
|
||||
<CardActionArea value={order.id} onClick={this.handleCardClick}>
|
||||
<CardContent>
|
||||
|
||||
<List dense="true">
|
||||
<ListItem >
|
||||
<ListItemAvatar >
|
||||
<Avatar
|
||||
alt={order.maker_nick}
|
||||
src={window.location.origin +'/static/assets/avatars/' + order.maker_nick + '.png'}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText>
|
||||
<Typography gutterBottom variant="h6">
|
||||
{order.maker_nick}
|
||||
</Typography>
|
||||
</ListItemText>
|
||||
</ListItem>
|
||||
|
||||
{/* CARD PARAGRAPH CONTENT */}
|
||||
<ListItemText>
|
||||
<Typography variant="subtitle1" color="text.secondary">
|
||||
◑{order.type == 0 ? <b> Buys </b>: <b> Sells </b>}
|
||||
<b>{parseFloat(parseFloat(order.amount).toFixed(4))}
|
||||
{" " +this.getCurrencyCode(order.currency)}</b> <a> worth of bitcoin</a>
|
||||
</Typography>
|
||||
|
||||
<Typography variant="subtitle1" color="text.secondary">
|
||||
◑ Payment via <b>{order.payment_method}</b>
|
||||
</Typography>
|
||||
|
||||
<Typography variant="subtitle1" color="text.secondary">
|
||||
◑ Priced {order.is_explicit ?
|
||||
" explicitly at " + this.pn(order.satoshis) + " Sats" : (
|
||||
" at " +
|
||||
parseFloat(parseFloat(order.premium).toFixed(4)) + "% over the market"
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
<Typography variant="subtitle1" color="text.secondary">
|
||||
◑ <b>{" 42,354 "}{this.getCurrencyCode(order.currency)}/BTC</b> (Binance API)
|
||||
</Typography>
|
||||
</ListItemText>
|
||||
|
||||
</List>
|
||||
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h5" variant="h5">
|
||||
You are {this.state.type == 0 ? " selling " : " buying "} BTC for {this.state.currencyCode}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Button color="secondary" variant="contained" to="/" component={Link}>
|
||||
Back
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
};
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup, Menu} from "@material-ui/core"
|
||||
import { Paper, Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup, Menu} from "@material-ui/core"
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
function getCookie(name) {
|
||||
@ -79,7 +79,7 @@ export default class MakerPage extends Component {
|
||||
premium: 0,
|
||||
});
|
||||
}
|
||||
handleClickisExplicit=(e)=>{
|
||||
handleClickExplicit=(e)=>{
|
||||
this.setState({
|
||||
isExplicit: true,
|
||||
satoshis: 10000,
|
||||
@ -104,130 +104,116 @@ export default class MakerPage extends Component {
|
||||
};
|
||||
fetch("/api/make/",requestOptions)
|
||||
.then((response) => response.json())
|
||||
.then((data) => this.props.history.push('/order/' + data.id));
|
||||
.then((data) => (console.log(data) & this.props.history.push('/order/' + data.id)));
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h4" variant="h4">
|
||||
Make an Order
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<FormControl component="fieldset">
|
||||
<RadioGroup row defaultValue="0" onChange={this.handleTypeChange}>
|
||||
<FormControlLabel
|
||||
value="0"
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h4" variant="h4">
|
||||
Make an Order
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Paper elevation={12} style={{ padding: 8,}}>
|
||||
<Grid item xs={12} align="center">
|
||||
<FormControl component="fieldset">
|
||||
<FormHelperText>
|
||||
<div align='center'>
|
||||
Choose Buy or Sell Bitcoin
|
||||
</div>
|
||||
</FormHelperText>
|
||||
<RadioGroup row defaultValue="0" onChange={this.handleTypeChange}>
|
||||
<FormControlLabel
|
||||
value="0"
|
||||
control={<Radio color="primary"/>}
|
||||
label="Buy"
|
||||
labelPlacement="Top"
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="1"
|
||||
control={<Radio color="secondary"/>}
|
||||
label="Sell"
|
||||
labelPlacement="Top"
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<FormControl >
|
||||
<TextField
|
||||
label="Amount of Fiat to Trade"
|
||||
type="number"
|
||||
required="true"
|
||||
defaultValue={this.defaultAmount}
|
||||
inputProps={{
|
||||
min:0 ,
|
||||
style: {textAlign:"center"}
|
||||
}}
|
||||
onChange={this.handleAmountChange}
|
||||
/>
|
||||
<Select
|
||||
label="Select Payment Currency"
|
||||
required="true"
|
||||
defaultValue={this.defaultCurrency}
|
||||
inputProps={{
|
||||
style: {textAlign:"center"}
|
||||
}}
|
||||
onChange={this.handleCurrencyChange}
|
||||
>
|
||||
<MenuItem value={1}>USD</MenuItem>
|
||||
<MenuItem value={2}>EUR</MenuItem>
|
||||
<MenuItem value={3}>ETH</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12} align="center">
|
||||
<FormControl >
|
||||
<TextField
|
||||
label="Payment Method(s)"
|
||||
type="text"
|
||||
require={true}
|
||||
inputProps={{
|
||||
style: {textAlign:"center"}
|
||||
}}
|
||||
onChange={this.handlePaymentMethodChange}
|
||||
/>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<FormControl component="fieldset">
|
||||
<RadioGroup row defaultValue="relative">
|
||||
<FormControlLabel
|
||||
value="relative"
|
||||
control={<Radio color="primary"/>}
|
||||
label="Buy"
|
||||
label="Relative"
|
||||
labelPlacement="Top"
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="1"
|
||||
onClick={this.handleClickRelative}
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="explicit"
|
||||
control={<Radio color="secondary"/>}
|
||||
label="Sell"
|
||||
label="Explicit"
|
||||
labelPlacement="Top"
|
||||
/>
|
||||
</RadioGroup>
|
||||
<FormHelperText>
|
||||
<div align='center'>
|
||||
Choose Buy or Sell Bitcoin
|
||||
</div>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<FormControl >
|
||||
<Select
|
||||
require={true}
|
||||
defaultValue={this.defaultCurrency}
|
||||
inputProps={{
|
||||
style: {textAlign:"center"}
|
||||
}}
|
||||
onChange={this.handleCurrencyChange}
|
||||
>
|
||||
<MenuItem value={1}>USD</MenuItem>
|
||||
<MenuItem value={2}>EUR</MenuItem>
|
||||
<MenuItem value={3}>ETH</MenuItem>
|
||||
</Select>
|
||||
<FormHelperText>
|
||||
<div align='center'>
|
||||
Select Payment Currency
|
||||
</div>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<FormControl >
|
||||
<TextField
|
||||
type="number"
|
||||
require={true}
|
||||
defaultValue={this.defaultAmount}
|
||||
inputProps={{
|
||||
min:0 ,
|
||||
style: {textAlign:"center"}
|
||||
}}
|
||||
onChange={this.handleAmountChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormHelperText>
|
||||
<div align='center'>
|
||||
Amount of Fiat to Trade
|
||||
</div>
|
||||
</FormHelperText>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<FormControl >
|
||||
<TextField
|
||||
type="text"
|
||||
require={true}
|
||||
inputProps={{
|
||||
style: {textAlign:"center"}
|
||||
}}
|
||||
onChange={this.handlePaymentMethodChange}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<div align='center'>
|
||||
Enter the Payment Method(s)
|
||||
</div>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<FormControl component="fieldset">
|
||||
<RadioGroup row defaultValue="relative">
|
||||
<FormControlLabel
|
||||
value="relative"
|
||||
control={<Radio color="primary"/>}
|
||||
label="Relative"
|
||||
labelPlacement="Top"
|
||||
onClick={this.handleClickRelative}
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="explicit"
|
||||
control={<Radio color="secondary"/>}
|
||||
label="Explicit"
|
||||
labelPlacement="Top"
|
||||
onClick={this.handleClickisExplicit}
|
||||
onShow="false"
|
||||
/>
|
||||
</RadioGroup>
|
||||
<FormHelperText >
|
||||
<div align='center'>
|
||||
Choose a Pricing Method
|
||||
</div>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
{/* conditional shows either Premium % field or Satoshis field based on pricing method */}
|
||||
{ this.state.isExplicit
|
||||
? <Grid item xs={12} align="center">
|
||||
<FormControl >
|
||||
<TextField
|
||||
onClick={this.handleClickExplicit}
|
||||
/>
|
||||
</RadioGroup>
|
||||
<FormHelperText >
|
||||
<div align='center'>
|
||||
Choose a Pricing Method
|
||||
</div>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
{/* conditional shows either Premium % field or Satoshis field based on pricing method */}
|
||||
{ this.state.isExplicit
|
||||
? <Grid item xs={12} align="center">
|
||||
<TextField
|
||||
label="Explicit Amount in Satoshis"
|
||||
type="number"
|
||||
require={true}
|
||||
required="true"
|
||||
inputProps={{
|
||||
// TODO read these from .env file
|
||||
min:10000 ,
|
||||
@ -235,36 +221,25 @@ export default class MakerPage extends Component {
|
||||
style: {textAlign:"center"}
|
||||
}}
|
||||
onChange={this.handleSatoshisChange}
|
||||
defaultValue={this.defaultSatoshis}
|
||||
// defaultValue={this.defaultSatoshis}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<div align='center'>
|
||||
Explicit Amount in Satoshis
|
||||
</div>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
: <Grid item xs={12} align="center">
|
||||
<FormControl >
|
||||
</Grid>
|
||||
: <Grid item xs={12} align="center">
|
||||
<TextField
|
||||
label="Premium over Market (%)"
|
||||
type="number"
|
||||
require={true}
|
||||
defaultValue={this.defaultPremium}
|
||||
// defaultValue={this.defaultPremium}
|
||||
inputProps={{
|
||||
style: {textAlign:"center"}
|
||||
}}
|
||||
onChange={this.handlePremiumChange}
|
||||
/>
|
||||
<FormHelperText>
|
||||
<div align='center'>
|
||||
Premium Relative to Market Price (%)
|
||||
</div>
|
||||
</FormHelperText>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
}
|
||||
</Grid>
|
||||
}
|
||||
</Paper>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Button color="primary" variant="contained" onClick={this.handleCreateOfferButtonPressed}>
|
||||
<Button color="primary" variant="contained" onClick={this.handleCreateOfferButtonPressed} >
|
||||
Create Order
|
||||
</Button>
|
||||
<Typography component="subtitle2" variant="subtitle2">
|
||||
@ -277,14 +252,13 @@ export default class MakerPage extends Component {
|
||||
}
|
||||
</div>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Button color="secondary" variant="contained" to="/" component={Link}>
|
||||
Back
|
||||
</Button>
|
||||
<Grid item xs={12} align="center">
|
||||
<Button color="secondary" variant="contained" to="/" component={Link}>
|
||||
Back
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
@ -1,7 +1,12 @@
|
||||
import React, { Component } from "react";
|
||||
import { Button , Grid, Typography, List, ListItem, ListItemText, ListItemAvatar, Avatar, Divider} from "@material-ui/core"
|
||||
import { Paper, Button , Grid, Typography, List, ListItem, ListItemText, ListItemAvatar, Avatar, Divider} from "@material-ui/core"
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
// pretty numbers
|
||||
function pn(x) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
}
|
||||
|
||||
export default class OrderPage extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -36,27 +41,31 @@ export default class OrderPage extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
// Fix to use proper react props
|
||||
handleClickBackButton=()=>{
|
||||
window.history.back();
|
||||
}
|
||||
render (){
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h5" variant="h5">
|
||||
Robosat BTC {this.state.type ? " Sell " : " Buy "} Order
|
||||
BTC {this.state.type ? " Sell " : " Buy "} Order
|
||||
</Typography>
|
||||
<Paper elevation={12} style={{ padding: 8,}}>
|
||||
<List component="nav" aria-label="mailbox folders">
|
||||
<ListItem>
|
||||
<ListItemAvatar sx={{ width: 56, height: 56 }}>
|
||||
<Avatar
|
||||
alt={this.state.makerNick}
|
||||
src={window.location.origin +'/static/assets/avatars/' + this.state.makerNick + '.png'}
|
||||
sx={{ width: 56, height: 56 }}
|
||||
/>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={this.state.makerNick} secondary="Order maker" />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText primary={this.state.amount+" "+this.state.currencyCode} secondary="Amount and currency requested"/>
|
||||
<ListItemText primary={parseFloat(parseFloat(this.state.amount).toFixed(4))+" "+this.state.currencyCode} secondary="Amount and currency requested"/>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
@ -65,9 +74,9 @@ export default class OrderPage extends Component {
|
||||
<Divider />
|
||||
<ListItem>
|
||||
{this.state.isExplicit ?
|
||||
<ListItemText primary={this.state.satoshis} secondary="Amount of Satoshis"/>
|
||||
<ListItemText primary={pn(this.state.satoshis)} secondary="Amount of Satoshis"/>
|
||||
:
|
||||
<ListItemText primary={this.state.premium} secondary="Premium over market price"/>
|
||||
<ListItemText primary={parseFloat(parseFloat(this.state.premium).toFixed(2))+"%"} secondary="Premium over market price"/>
|
||||
}
|
||||
</ListItem>
|
||||
<Divider />
|
||||
@ -93,6 +102,10 @@ export default class OrderPage extends Component {
|
||||
<Grid item xs={12} align="center">
|
||||
{this.state.isParticipant ? "" : <Button variant='contained' color='primary' to='/home' component={Link}>Take Order</Button>}
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
{this.state.isParticipant ? "" : <Button variant='contained' color='secondary' onClick={this.handleClickBackButton}>Back</Button>}
|
||||
</Grid>
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { Component } from "react";
|
||||
import { Button , Grid, Typography, TextField, Select, FormHelperText, MenuItem, FormControl, Radio, FormControlLabel, RadioGroup, Menu} from "@material-ui/core"
|
||||
import { Button , Grid, Typography, TextField, ButtonGroup} from "@material-ui/core"
|
||||
import { Link } from 'react-router-dom'
|
||||
import Image from 'material-ui-image'
|
||||
|
||||
@ -85,9 +85,39 @@ export default class UserGenPage extends Component {
|
||||
this.getGeneratedUser();
|
||||
}
|
||||
|
||||
// TO FIX CSRF TOKEN IS NOT UPDATED UNTIL WINDOW IS RELOADED
|
||||
reload_for_csrf_to_work=()=>{
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h5" variant="h5">
|
||||
<b>{this.state.nickname ? "⚡"+this.state.nickname+"⚡" : ""}</b>
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<div style={{ maxWidth: 200, maxHeight: 200 }}>
|
||||
<Image className='newAvatar'
|
||||
disableError='true'
|
||||
cover='true'
|
||||
color='null'
|
||||
src={this.state.avatar_url}
|
||||
/>
|
||||
</div><br/>
|
||||
</Grid>
|
||||
{
|
||||
this.state.found ?
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="subtitle2" variant="subtitle2" color='primary'>
|
||||
{this.state.found}<br/>
|
||||
</Typography>
|
||||
</Grid>
|
||||
:
|
||||
""
|
||||
}
|
||||
<Grid item xs={12} align="center">
|
||||
<TextField
|
||||
error={this.state.bad_request}
|
||||
@ -102,36 +132,19 @@ export default class UserGenPage extends Component {
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<div style={{ maxWidth: 200, maxHeight: 200 }}>
|
||||
<Image className='newAvatar'
|
||||
disableError='true'
|
||||
cover='true'
|
||||
color='null'
|
||||
src={this.state.avatar_url}
|
||||
/>
|
||||
</div>
|
||||
<Button onClick={this.handleAnotherButtonPressed}>Generate Another Robosat</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<ButtonGroup variant="contained" aria-label="outlined primary button group">
|
||||
<Button color='primary' to='/make/' component={Link}>Make Order</Button>
|
||||
<Button to='/home' component={Link}>INFO</Button>
|
||||
<Button color='secondary' to='/book/' component={Link}>View Book</Button>
|
||||
</ButtonGroup>
|
||||
</Grid>
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="h5" variant="h5">
|
||||
<b>{this.state.nickname ? "⚡"+this.state.nickname+"⚡" : ""}</b>
|
||||
</Typography>
|
||||
</Grid>
|
||||
{
|
||||
this.state.found ?
|
||||
<Grid item xs={12} align="center">
|
||||
<Typography component="subtitle2" variant="subtitle2" color='primary'>
|
||||
{this.state.found}<br/>
|
||||
</Typography>
|
||||
<Button variant='contained' color='primary' to='/home' component={Link}>Cool!</Button>
|
||||
</Grid>
|
||||
:
|
||||
<Grid item xs={12} align="center">
|
||||
<Button variant='contained' color='primary' to='/home' component={Link}>Take This Robosat!</Button>
|
||||
</Grid>
|
||||
}
|
||||
|
||||
<Grid item xs={12} align="center">
|
||||
<Button variant='contained' to='/' component={Link} onClick={this.handleAnotherButtonPressed}>Give Me Another</Button>
|
||||
Easy and Private Lightning peer-to-peer Exchange
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
|
@ -1,15 +0,0 @@
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
3
setup.md
3
setup.md
@ -47,7 +47,7 @@ pip install robohash
|
||||
### Install npm
|
||||
`sudo apt install npm`
|
||||
|
||||
No need to repeat, this is the list of npm packages we use
|
||||
npm packages we use
|
||||
```
|
||||
cd frontend
|
||||
npm init -y
|
||||
@ -59,6 +59,7 @@ npm install @babel/plugin-proposal-class-properties
|
||||
npm install react-router-dom@5.2.0
|
||||
npm install @material-ui/icons
|
||||
npm install material-ui-image
|
||||
npm install @mui/system @emotion/react @emotion/styled
|
||||
```
|
||||
|
||||
### Launch the React render
|
||||
|
Loading…
Reference in New Issue
Block a user