Merge pull request 'feature/vendor' (#1) from feature/vendor into main
Reviewed-on: http://172.16.100.2:3000/mike/django/pulls/1
This commit is contained in:
commit
79ec088059
|
@ -1,3 +1,18 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
|
from .models import VendorsData, CryptoCoins, VendorsAddresses
|
||||||
|
|
||||||
|
class VendorDetails(admin.ModelAdmin):
|
||||||
|
fields = ["vendor"]
|
||||||
|
list_display = ("vendorid_id", "vendor", "vendorWebName", "vendorWebAddr", "vendorAddDate", "vendorIsActive")
|
||||||
|
|
||||||
|
class CryptoDetails(admin.ModelAdmin):
|
||||||
|
list_display = ("coinName", "coinSymbol", "coinAddDate", "coinIsActive" )
|
||||||
|
|
||||||
|
class VendorAddressDetails(admin.ModelAdmin):
|
||||||
|
list_display = ("coin", "address", "vendor_id", "vendorid_id", "addrIsActive")
|
||||||
|
|
||||||
|
admin.site.register(VendorsData, VendorDetails)
|
||||||
|
admin.site.register(CryptoCoins, CryptoDetails)
|
||||||
|
admin.site.register(VendorsAddresses, VendorAddressDetails)
|
|
@ -0,0 +1,12 @@
|
||||||
|
from django.forms import ModelForm
|
||||||
|
from .models import VendorsData, VendorsAddresses
|
||||||
|
|
||||||
|
class VendorEditForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = VendorsData
|
||||||
|
fields = ['vendorCoverAmount', 'vendorPaidNotification', 'vendorPayWindow', 'vendorWebName', 'vendorWebAddr', 'vendorNetworkFee']
|
||||||
|
|
||||||
|
class VendorAddrAddForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = VendorsAddresses
|
||||||
|
fields = ['coin', 'address']
|
|
@ -0,0 +1,629 @@
|
||||||
|
import string, hashlib, binascii, random, socket, base58, sys
|
||||||
|
from typing import Union
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
from ipaddress import ip_address, ip_network, IPv4Address
|
||||||
|
from base58 import b58decode_check, b58encode_check
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
def vendor_generator(size=6, chars=string.ascii_uppercase + string.digits):
|
||||||
|
return ''.join(random.choice(chars) for _ in range(size))
|
||||||
|
|
||||||
|
|
||||||
|
def decodeBase58(address):
|
||||||
|
decoded = base58.b58decode(address).hex()
|
||||||
|
prefixAndHash = decoded[:len(decoded)-8]
|
||||||
|
checksum = decoded[len(decoded)-8:]
|
||||||
|
hash = prefixAndHash
|
||||||
|
for _ in range(1,3):
|
||||||
|
hash = hashlib.sha256(binascii.unhexlify(hash)).hexdigest()
|
||||||
|
if(checksum == hash[:8]):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def decodeMonero(address):
|
||||||
|
# decode monero address
|
||||||
|
if len(address) == 95 or len(address) == 106:
|
||||||
|
# Decode the address from Base58
|
||||||
|
decoded = decode(address)
|
||||||
|
|
||||||
|
# Verify address type byte
|
||||||
|
if decoded[0:2] not in ["12", "2a", "13"]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Verify checksum
|
||||||
|
# later
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def checksumCheck(method, address):
|
||||||
|
match method.lower():
|
||||||
|
case 'btc':
|
||||||
|
return decodeBase58(address) if address[0] == '1' or address[0] == '3' else True if address[0:3] == 'bc1' and decode("bc", address)[0] != None else False
|
||||||
|
case 'btct':
|
||||||
|
return decodeBase58(address) if address[0] == '2' else True if address[0:3] == 'tb1' and decode("tb", address)[0] != None else False
|
||||||
|
case 'ltc':
|
||||||
|
return decodeBase58(address) if address[0] == '3' or address[0] == 'M' or address[0] == 'L' else True if address[0:4] == 'ltc1' and decode("ltc", address)[0] != None else False
|
||||||
|
case 'bch':
|
||||||
|
return is_valid(address) if address[0] == '1' else True if is_valid('bitcoincash:'+address) == True else False
|
||||||
|
case 'zec':
|
||||||
|
return decodeBase58(address) if address[0] == 't' or address[0] == 'z' else False
|
||||||
|
case 'xmr':
|
||||||
|
#needs new function to check if address is valid, a decoder maybe
|
||||||
|
return decodeMonero(address)
|
||||||
|
case _:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#########################################################################
|
||||||
|
## UrlValidator
|
||||||
|
#########################################################################
|
||||||
|
|
||||||
|
class UrlValidator:
|
||||||
|
@staticmethod
|
||||||
|
def is_internal_address(ip: Union[IPv4Address]) -> bool:
|
||||||
|
return any([
|
||||||
|
ip.is_private,
|
||||||
|
ip.is_unspecified,
|
||||||
|
ip.is_reserved,
|
||||||
|
ip.is_loopback,
|
||||||
|
ip.is_multicast,
|
||||||
|
ip.is_link_local,
|
||||||
|
])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def validate(cls, url: str):
|
||||||
|
DEFAULT_PORT_WHITELIST = {80, 81, 8080, 443, 8443, 8000}
|
||||||
|
DEFAULT_SCHEME_WHITELIST = {'http', 'https'}
|
||||||
|
DEFAULT_HOST_BLACKLIST = {'192.0.0.192', '169.254.169.254', '100.100.100.200', 'metadata.packet.net', 'metadata.google.internal'}
|
||||||
|
DEFAULT_CHARACTER_WHITELIST = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:/-_.?&='
|
||||||
|
|
||||||
|
if url is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
whitelist_set = set(DEFAULT_CHARACTER_WHITELIST)
|
||||||
|
if any(c not in whitelist_set for c in url):
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
ip = ip_address(url)
|
||||||
|
except ValueError:
|
||||||
|
try:
|
||||||
|
host = urlparse(url).hostname
|
||||||
|
ip = ip_address(str(socket.gethostbyname(host)))
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
port_whitelist = DEFAULT_PORT_WHITELIST.copy()
|
||||||
|
scheme_whitelist = DEFAULT_SCHEME_WHITELIST.copy()
|
||||||
|
host_blacklist = DEFAULT_HOST_BLACKLIST.copy()
|
||||||
|
|
||||||
|
try:
|
||||||
|
port, scheme = urlparse(url).port, urlparse(url).scheme
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if scheme_whitelist and scheme is not None and scheme not in scheme_whitelist:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if host_blacklist and host is not None and host in host_blacklist:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if port_whitelist and port is not None and port not in port_whitelist:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if ip.version == 4:
|
||||||
|
if not ip.is_private:
|
||||||
|
# CGNAT IPs do not set `is_private` so `not is_global` added
|
||||||
|
if not ip_network(ip).is_global:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if cls.is_internal_address(ip):
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
"""Reference implementation for Bech32/Bech32m and segwit addresses."""
|
||||||
|
#########################################################################
|
||||||
|
## SEGWIT
|
||||||
|
#########################################################################
|
||||||
|
|
||||||
|
class Encoding(Enum):
|
||||||
|
"""Enumeration type to list the various supported encodings."""
|
||||||
|
BECH32 = 1
|
||||||
|
BECH32M = 2
|
||||||
|
|
||||||
|
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
||||||
|
BECH32M_CONST = 0x2bc830a3
|
||||||
|
|
||||||
|
def bech32_polymod(values):
|
||||||
|
"""Internal function that computes the Bech32 checksum."""
|
||||||
|
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
|
||||||
|
chk = 1
|
||||||
|
for value in values:
|
||||||
|
top = chk >> 25
|
||||||
|
chk = (chk & 0x1ffffff) << 5 ^ value
|
||||||
|
for i in range(5):
|
||||||
|
chk ^= generator[i] if ((top >> i) & 1) else 0
|
||||||
|
return chk
|
||||||
|
|
||||||
|
|
||||||
|
def bech32_hrp_expand(hrp):
|
||||||
|
"""Expand the HRP into values for checksum computation."""
|
||||||
|
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
|
||||||
|
|
||||||
|
|
||||||
|
def bech32_verify_checksum(hrp, data):
|
||||||
|
"""Verify a checksum given HRP and converted data characters."""
|
||||||
|
const = bech32_polymod(bech32_hrp_expand(hrp) + data)
|
||||||
|
if const == 1:
|
||||||
|
return Encoding.BECH32
|
||||||
|
if const == BECH32M_CONST:
|
||||||
|
return Encoding.BECH32M
|
||||||
|
return None
|
||||||
|
|
||||||
|
def bech32_create_checksum(hrp, data, spec):
|
||||||
|
"""Compute the checksum values given HRP and data."""
|
||||||
|
values = bech32_hrp_expand(hrp) + data
|
||||||
|
const = BECH32M_CONST if spec == Encoding.BECH32M else 1
|
||||||
|
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ const
|
||||||
|
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
|
||||||
|
|
||||||
|
|
||||||
|
def bech32_encode(hrp, data, spec):
|
||||||
|
"""Compute a Bech32 string given HRP and data values."""
|
||||||
|
combined = data + bech32_create_checksum(hrp, data, spec)
|
||||||
|
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
|
||||||
|
|
||||||
|
def bech32_decode(bech):
|
||||||
|
"""Validate a Bech32/Bech32m string, and determine HRP and data."""
|
||||||
|
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
|
||||||
|
(bech.lower() != bech and bech.upper() != bech)):
|
||||||
|
return (None, None, None)
|
||||||
|
bech = bech.lower()
|
||||||
|
pos = bech.rfind('1')
|
||||||
|
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
|
||||||
|
return (None, None, None)
|
||||||
|
if not all(x in CHARSET for x in bech[pos+1:]):
|
||||||
|
return (None, None, None)
|
||||||
|
hrp = bech[:pos]
|
||||||
|
data = [CHARSET.find(x) for x in bech[pos+1:]]
|
||||||
|
spec = bech32_verify_checksum(hrp, data)
|
||||||
|
if spec is None:
|
||||||
|
return (None, None, None)
|
||||||
|
return (hrp, data[:-6], spec)
|
||||||
|
|
||||||
|
def convertbits(data, frombits, tobits, pad=True):
|
||||||
|
"""General power-of-2 base conversion."""
|
||||||
|
acc = 0
|
||||||
|
bits = 0
|
||||||
|
ret = []
|
||||||
|
maxv = (1 << tobits) - 1
|
||||||
|
max_acc = (1 << (frombits + tobits - 1)) - 1
|
||||||
|
for value in data:
|
||||||
|
if value < 0 or (value >> frombits):
|
||||||
|
return None
|
||||||
|
acc = ((acc << frombits) | value) & max_acc
|
||||||
|
bits += frombits
|
||||||
|
while bits >= tobits:
|
||||||
|
bits -= tobits
|
||||||
|
ret.append((acc >> bits) & maxv)
|
||||||
|
if pad:
|
||||||
|
if bits:
|
||||||
|
ret.append((acc << (tobits - bits)) & maxv)
|
||||||
|
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
|
||||||
|
return None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def decode(hrp, addr):
|
||||||
|
"""Decode a segwit address."""
|
||||||
|
hrpgot, data, spec = bech32_decode(addr)
|
||||||
|
if hrpgot != hrp:
|
||||||
|
return (None, None)
|
||||||
|
decoded = convertbits(data[1:], 5, 8, False)
|
||||||
|
if decoded is None or len(decoded) < 2 or len(decoded) > 40:
|
||||||
|
return (None, None)
|
||||||
|
if data[0] > 16:
|
||||||
|
return (None, None)
|
||||||
|
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
|
||||||
|
return (None, None)
|
||||||
|
if data[0] == 0 and spec != Encoding.BECH32 or data[0] != 0 and spec != Encoding.BECH32M:
|
||||||
|
return (None, None)
|
||||||
|
return (data[0], decoded)
|
||||||
|
|
||||||
|
|
||||||
|
def encode(hrp, witver, witprog):
|
||||||
|
"""Encode a segwit address."""
|
||||||
|
spec = Encoding.BECH32 if witver == 0 else Encoding.BECH32M
|
||||||
|
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5), spec)
|
||||||
|
if decode(hrp, ret) == (None, None):
|
||||||
|
return None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
#########################################################################
|
||||||
|
## CRYPTO
|
||||||
|
#########################################################################
|
||||||
|
CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
|
||||||
|
|
||||||
|
def polymod(values):
|
||||||
|
chk = 1
|
||||||
|
generator = [
|
||||||
|
(0x01, 0x98f2bc8e61),
|
||||||
|
(0x02, 0x79b76d99e2),
|
||||||
|
(0x04, 0xf33e5fb3c4),
|
||||||
|
(0x08, 0xae2eabe2a8),
|
||||||
|
(0x10, 0x1e4f43e470)]
|
||||||
|
for value in values:
|
||||||
|
top = chk >> 35
|
||||||
|
chk = ((chk & 0x07ffffffff) << 5) ^ value
|
||||||
|
for i in generator:
|
||||||
|
if top & i[0] != 0:
|
||||||
|
chk ^= i[1]
|
||||||
|
return chk ^ 1
|
||||||
|
|
||||||
|
|
||||||
|
def prefix_expand(prefix):
|
||||||
|
return [ord(x) & 0x1f for x in prefix] + [0]
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_checksum(prefix, payload):
|
||||||
|
poly = polymod(prefix_expand(prefix) + payload + [0, 0, 0, 0, 0, 0, 0, 0])
|
||||||
|
out = list()
|
||||||
|
for i in range(8):
|
||||||
|
out.append((poly >> 5 * (7 - i)) & 0x1f)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def verify_checksum(prefix, payload):
|
||||||
|
return polymod(prefix_expand(prefix) + payload) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def b32decode(inputs):
|
||||||
|
out = list()
|
||||||
|
for letter in inputs:
|
||||||
|
out.append(CHARSET.find(letter))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def b32encode(inputs):
|
||||||
|
out = ''
|
||||||
|
for char_code in inputs:
|
||||||
|
out += CHARSET[char_code]
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def convertbits(data, frombits, tobits, pad=True):
|
||||||
|
acc = 0
|
||||||
|
bits = 0
|
||||||
|
ret = []
|
||||||
|
maxv = (1 << tobits) - 1
|
||||||
|
max_acc = (1 << (frombits + tobits - 1)) - 1
|
||||||
|
for value in data:
|
||||||
|
if value < 0 or (value >> frombits):
|
||||||
|
return None
|
||||||
|
acc = ((acc << frombits) | value) & max_acc
|
||||||
|
bits += frombits
|
||||||
|
while bits >= tobits:
|
||||||
|
bits -= tobits
|
||||||
|
ret.append((acc >> bits) & maxv)
|
||||||
|
if pad:
|
||||||
|
if bits:
|
||||||
|
ret.append((acc << (tobits - bits)) & maxv)
|
||||||
|
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
|
||||||
|
return None
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
#########################################################################
|
||||||
|
## BCH CONVERT
|
||||||
|
#########################################################################
|
||||||
|
class InvalidAddress(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Address:
|
||||||
|
VERSION_MAP = {
|
||||||
|
'legacy': [
|
||||||
|
('P2SH', 5, False),
|
||||||
|
('P2PKH', 0, False),
|
||||||
|
('P2SH-TESTNET', 196, True),
|
||||||
|
('P2PKH-TESTNET', 111, True)
|
||||||
|
],
|
||||||
|
'cash': [
|
||||||
|
('P2SH', 8, False),
|
||||||
|
('P2PKH', 0, False),
|
||||||
|
('P2SH-TESTNET', 8, True),
|
||||||
|
('P2PKH-TESTNET', 0, True)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
MAINNET_PREFIX = 'bitcoincash'
|
||||||
|
TESTNET_PREFIX = 'bchtest'
|
||||||
|
|
||||||
|
def __init__(self, version, payload, prefix=None):
|
||||||
|
self.version = version
|
||||||
|
self.payload = payload
|
||||||
|
if prefix:
|
||||||
|
self.prefix = prefix
|
||||||
|
else:
|
||||||
|
if Address._address_type('cash', self.version)[2]:
|
||||||
|
self.prefix = self.TESTNET_PREFIX
|
||||||
|
else:
|
||||||
|
self.prefix = self.MAINNET_PREFIX
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'version: {}\npayload: {}\nprefix: {}'.format(self.version, self.payload, self.prefix)
|
||||||
|
|
||||||
|
def legacy_address(self):
|
||||||
|
version_int = Address._address_type('legacy', self.version)[1]
|
||||||
|
return b58encode_check(Address.code_list_to_string([version_int] + self.payload))
|
||||||
|
|
||||||
|
def cash_address(self):
|
||||||
|
version_int = Address._address_type('cash', self.version)[1]
|
||||||
|
payload = [version_int] + self.payload
|
||||||
|
payload = convertbits(payload, 8, 5)
|
||||||
|
checksum = calculate_checksum(self.prefix, payload)
|
||||||
|
return self.prefix + ':' + b32encode(payload + checksum)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def code_list_to_string(code_list):
|
||||||
|
if sys.version_info > (3, 0):
|
||||||
|
output = bytes()
|
||||||
|
for code in code_list:
|
||||||
|
output += bytes([code])
|
||||||
|
else:
|
||||||
|
output = ''
|
||||||
|
for code in code_list:
|
||||||
|
output += chr(code)
|
||||||
|
return output
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _address_type(address_type, version):
|
||||||
|
for mapping in Address.VERSION_MAP[address_type]:
|
||||||
|
if mapping[0] == version or mapping[1] == version:
|
||||||
|
return mapping
|
||||||
|
raise InvalidAddress('Could not determine address version')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_string(address_string):
|
||||||
|
try:
|
||||||
|
address_string = str(address_string)
|
||||||
|
except Exception:
|
||||||
|
raise InvalidAddress('Expected string as input')
|
||||||
|
if ':' not in address_string:
|
||||||
|
return Address._legacy_string(address_string)
|
||||||
|
else:
|
||||||
|
return Address._cash_string(address_string)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _legacy_string(address_string):
|
||||||
|
try:
|
||||||
|
decoded = bytearray(b58decode_check(address_string))
|
||||||
|
except ValueError:
|
||||||
|
raise InvalidAddress('Could not decode legacy address')
|
||||||
|
version = Address._address_type('legacy', decoded[0])[0]
|
||||||
|
payload = list()
|
||||||
|
for letter in decoded[1:]:
|
||||||
|
payload.append(letter)
|
||||||
|
return Address(version, payload)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _cash_string(address_string):
|
||||||
|
if address_string.upper() != address_string and address_string.lower() != address_string:
|
||||||
|
raise InvalidAddress('Cash address contains uppercase and lowercase characters')
|
||||||
|
address_string = address_string.lower()
|
||||||
|
colon_count = address_string.count(':')
|
||||||
|
if colon_count == 0:
|
||||||
|
address_string = Address.MAINNET_PREFIX + ':' + address_string
|
||||||
|
elif colon_count > 1:
|
||||||
|
raise InvalidAddress('Cash address contains more than one colon character')
|
||||||
|
prefix, base32string = address_string.split(':')
|
||||||
|
decoded = b32decode(base32string)
|
||||||
|
if not verify_checksum(prefix, decoded):
|
||||||
|
raise InvalidAddress('Bad cash address checksum')
|
||||||
|
converted = convertbits(decoded, 5, 8)
|
||||||
|
version = Address._address_type('cash', converted[0])[0]
|
||||||
|
if prefix == Address.TESTNET_PREFIX:
|
||||||
|
version += '-TESTNET'
|
||||||
|
payload = converted[1:-6]
|
||||||
|
return Address(version, payload, prefix)
|
||||||
|
|
||||||
|
|
||||||
|
def to_cash_address(address):
|
||||||
|
return Address.from_string(address).cash_address()
|
||||||
|
|
||||||
|
|
||||||
|
def to_legacy_address(address):
|
||||||
|
return Address.from_string(address).legacy_address()
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid(address):
|
||||||
|
try:
|
||||||
|
Address.from_string(address)
|
||||||
|
return True
|
||||||
|
except InvalidAddress:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#########################################################################
|
||||||
|
## monero
|
||||||
|
#########################################################################
|
||||||
|
|
||||||
|
|
||||||
|
__alphabet = [
|
||||||
|
ord(s) for s in "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
|
||||||
|
]
|
||||||
|
__b58base = 58
|
||||||
|
__UINT64MAX = 2 ** 64
|
||||||
|
__encodedBlockSizes = [0, 2, 3, 5, 6, 7, 9, 10, 11]
|
||||||
|
__fullBlockSize = 8
|
||||||
|
__fullEncodedBlockSize = 11
|
||||||
|
|
||||||
|
|
||||||
|
def _hexToBin(hex_):
|
||||||
|
if len(hex_) % 2 != 0:
|
||||||
|
raise ValueError("Hex string has invalid length: %d" % len(hex_))
|
||||||
|
return [int(hex_[i : i + 2], 16) for i in range(0, len(hex_), 2)]
|
||||||
|
|
||||||
|
|
||||||
|
def _binToHex(bin_):
|
||||||
|
return "".join("%02x" % int(b) for b in bin_)
|
||||||
|
|
||||||
|
|
||||||
|
def _uint8be_to_64(data):
|
||||||
|
if not (1 <= len(data) <= 8):
|
||||||
|
raise ValueError("Invalid input length: %d" % len(data))
|
||||||
|
|
||||||
|
res = 0
|
||||||
|
for b in data:
|
||||||
|
res = res << 8 | b
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def _uint64_to_8be(num, size):
|
||||||
|
if size < 1 or size > 8:
|
||||||
|
raise ValueError("Invalid input length: %d" % size)
|
||||||
|
res = [0] * size
|
||||||
|
|
||||||
|
twopow8 = 2 ** 8
|
||||||
|
for i in range(size - 1, -1, -1):
|
||||||
|
res[i] = num % twopow8
|
||||||
|
num = num // twopow8
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def encode_block(data, buf, index):
|
||||||
|
l_data = len(data)
|
||||||
|
|
||||||
|
if l_data < 1 or l_data > __fullEncodedBlockSize:
|
||||||
|
raise ValueError("Invalid block length: %d" % l_data)
|
||||||
|
|
||||||
|
num = _uint8be_to_64(data)
|
||||||
|
i = __encodedBlockSizes[l_data] - 1
|
||||||
|
|
||||||
|
while num > 0:
|
||||||
|
remainder = num % __b58base
|
||||||
|
num = num // __b58base
|
||||||
|
buf[index + i] = __alphabet[remainder]
|
||||||
|
i -= 1
|
||||||
|
|
||||||
|
return buf
|
||||||
|
|
||||||
|
|
||||||
|
def encode(hex):
|
||||||
|
"""Encode hexadecimal string as base58 (ex: encoding a Monero address)."""
|
||||||
|
data = _hexToBin(hex)
|
||||||
|
l_data = len(data)
|
||||||
|
|
||||||
|
if l_data == 0:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
full_block_count = l_data // __fullBlockSize
|
||||||
|
last_block_size = l_data % __fullBlockSize
|
||||||
|
res_size = (
|
||||||
|
full_block_count * __fullEncodedBlockSize + __encodedBlockSizes[last_block_size]
|
||||||
|
)
|
||||||
|
|
||||||
|
res = bytearray([__alphabet[0]] * res_size)
|
||||||
|
|
||||||
|
for i in range(full_block_count):
|
||||||
|
res = encode_block(
|
||||||
|
data[(i * __fullBlockSize) : (i * __fullBlockSize + __fullBlockSize)],
|
||||||
|
res,
|
||||||
|
i * __fullEncodedBlockSize,
|
||||||
|
)
|
||||||
|
|
||||||
|
if last_block_size > 0:
|
||||||
|
res = encode_block(
|
||||||
|
data[
|
||||||
|
(full_block_count * __fullBlockSize) : (
|
||||||
|
full_block_count * __fullBlockSize + last_block_size
|
||||||
|
)
|
||||||
|
],
|
||||||
|
res,
|
||||||
|
full_block_count * __fullEncodedBlockSize,
|
||||||
|
)
|
||||||
|
|
||||||
|
return bytes(res).decode("ascii")
|
||||||
|
|
||||||
|
|
||||||
|
def decode_block(data, buf, index):
|
||||||
|
l_data = len(data)
|
||||||
|
|
||||||
|
if l_data < 1 or l_data > __fullEncodedBlockSize:
|
||||||
|
raise ValueError("Invalid block length: %d" % l_data)
|
||||||
|
|
||||||
|
res_size = __encodedBlockSizes.index(l_data)
|
||||||
|
if res_size <= 0:
|
||||||
|
raise ValueError("Invalid block size: %d" % res_size)
|
||||||
|
|
||||||
|
res_num = 0
|
||||||
|
order = 1
|
||||||
|
for i in range(l_data - 1, -1, -1):
|
||||||
|
digit = __alphabet.index(data[i])
|
||||||
|
if digit < 0:
|
||||||
|
raise ValueError("Invalid symbol: %s" % data[i])
|
||||||
|
|
||||||
|
product = order * digit + res_num
|
||||||
|
if product > __UINT64MAX:
|
||||||
|
raise ValueError(
|
||||||
|
"Overflow: %d * %d + %d = %d" % (order, digit, res_num, product)
|
||||||
|
)
|
||||||
|
|
||||||
|
res_num = product
|
||||||
|
order = order * __b58base
|
||||||
|
|
||||||
|
if res_size < __fullBlockSize and 2 ** (8 * res_size) <= res_num:
|
||||||
|
raise ValueError("Overflow: %d doesn't fit in %d bit(s)" % (res_num, res_size))
|
||||||
|
|
||||||
|
tmp_buf = _uint64_to_8be(res_num, res_size)
|
||||||
|
buf[index : index + len(tmp_buf)] = tmp_buf
|
||||||
|
|
||||||
|
return buf
|
||||||
|
|
||||||
|
|
||||||
|
def decode(enc):
|
||||||
|
"""Decode a base58 string (ex: a Monero address) into hexidecimal form."""
|
||||||
|
enc = bytearray(enc, encoding="ascii")
|
||||||
|
l_enc = len(enc)
|
||||||
|
|
||||||
|
if l_enc == 0:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
full_block_count = l_enc // __fullEncodedBlockSize
|
||||||
|
last_block_size = l_enc % __fullEncodedBlockSize
|
||||||
|
try:
|
||||||
|
last_block_decoded_size = __encodedBlockSizes.index(last_block_size)
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError("Invalid encoded length: %d" % l_enc)
|
||||||
|
|
||||||
|
data_size = full_block_count * __fullBlockSize + last_block_decoded_size
|
||||||
|
|
||||||
|
data = bytearray(data_size)
|
||||||
|
for i in range(full_block_count):
|
||||||
|
data = decode_block(
|
||||||
|
enc[
|
||||||
|
(i * __fullEncodedBlockSize) : (
|
||||||
|
i * __fullEncodedBlockSize + __fullEncodedBlockSize
|
||||||
|
)
|
||||||
|
],
|
||||||
|
data,
|
||||||
|
i * __fullBlockSize,
|
||||||
|
)
|
||||||
|
|
||||||
|
if last_block_size > 0:
|
||||||
|
data = decode_block(
|
||||||
|
enc[
|
||||||
|
(full_block_count * __fullEncodedBlockSize) : (
|
||||||
|
full_block_count * __fullEncodedBlockSize + last_block_size
|
||||||
|
)
|
||||||
|
],
|
||||||
|
data,
|
||||||
|
full_block_count * __fullBlockSize,
|
||||||
|
)
|
||||||
|
|
||||||
|
return _binToHex(data)
|
||||||
|
|
||||||
|
#########################################################################
|
||||||
|
#########################################################################
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2 on 2023-04-29 14:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('base', '0004_vendorsdata_vendoruuid'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vendorsdata',
|
||||||
|
name='vendorIsActive',
|
||||||
|
field=models.BooleanField(default=1),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Generated by Django 4.2 on 2023-04-29 14:02
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('base', '0005_alter_vendorsdata_vendorisactive'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vendorsdata',
|
||||||
|
name='vendorDeleted',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vendorsdata',
|
||||||
|
name='vendorPaidNotification',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vendorsdata',
|
||||||
|
name='vendorPayWindow',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vendorsdata',
|
||||||
|
name='vendorSkipScreen',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Generated by Django 4.2 on 2023-04-29 17:46
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('base', '0006_alter_vendorsdata_vendordeleted_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='vendorsdata',
|
||||||
|
name='vendorUpdated',
|
||||||
|
field=models.DateTimeField(auto_now=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vendorsdata',
|
||||||
|
name='vendorAddDelete',
|
||||||
|
field=models.DateTimeField(auto_now_add=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2 on 2023-05-02 12:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('base', '0007_vendorsdata_vendorupdated_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vendorsdata',
|
||||||
|
name='vendorWebAddr',
|
||||||
|
field=models.URLField(max_length=250),
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 4.2 on 2023-05-02 15:16
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('base', '0008_alter_vendorsdata_vendorwebaddr'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='vendorsdata',
|
||||||
|
name='id',
|
||||||
|
field=models.BigAutoField(primary_key=True, serialize=False),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,30 +1,74 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from users.models import User
|
from users.models import User
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
|
class CryptoCoins(models.Model):
|
||||||
|
id = models.BigAutoField(primary_key=True)
|
||||||
|
coinName = models.CharField(max_length=250)
|
||||||
|
coinSymbol = models.CharField(max_length=10)
|
||||||
|
coinIsActive = models.BooleanField(default=True)
|
||||||
|
coinAddDate = models.DateTimeField(auto_now_add=True)
|
||||||
|
coinUpdated = models.DateTimeField(auto_now=True)
|
||||||
|
coinDelete = models.DateTimeField(auto_now_add=True, blank=True, null=True)
|
||||||
|
coinDeleted = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Crypto Coin"
|
||||||
|
verbose_name_plural = "Crypto Coins"
|
||||||
|
ordering = ["-id"]
|
||||||
|
|
||||||
class VendorsData(models.Model):
|
class VendorsData(models.Model):
|
||||||
|
id = models.BigAutoField(primary_key=True)
|
||||||
vendorid = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
|
vendorid = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
|
||||||
vendorUUID = models.UUIDField()
|
vendorUUID = models.UUIDField()
|
||||||
vendor = models.CharField(max_length=8, unique=True)
|
vendor = models.CharField(max_length=8, unique=True)
|
||||||
vendorSecretKey = models.UUIDField()
|
vendorSecretKey = models.CharField(max_length=250)
|
||||||
vendorCoverAmount = models.IntegerField(default=0)
|
vendorCoverAmount = models.IntegerField(default=0)
|
||||||
vendorPaidNotification = models.IntegerField(default=1)
|
vendorNetworkFee = models.IntegerField(default=0)
|
||||||
vendorSkipScreen = models.IntegerField(default=0)
|
vendorPaidNotification = models.BooleanField(default=True)
|
||||||
vendorPayWindow = models.IntegerField(default=0)
|
vendorSkipScreen = models.BooleanField(default=False)
|
||||||
|
vendorPayWindow = models.BooleanField(default=True)
|
||||||
vendorWebName = models.CharField(max_length=250)
|
vendorWebName = models.CharField(max_length=250)
|
||||||
vendorWebAddr = models.CharField(max_length=250)
|
vendorWebAddr = models.URLField(max_length=250)
|
||||||
vendorAddDate = models.DateTimeField(auto_now_add=True)
|
vendorAddDate = models.DateTimeField(auto_now_add=True)
|
||||||
vendorDeleted = models.IntegerField(default=0)
|
vendorDeleted = models.BooleanField(default=False)
|
||||||
vendorAddDelete = models.DateTimeField(auto_now=True)
|
vendorUpdated = models.DateTimeField(auto_now=True)
|
||||||
vendorIsActive = models.IntegerField(default=1)
|
vendorAddDelete = models.DateTimeField(auto_now_add=True)
|
||||||
|
vendorIsActive = models.BooleanField(default=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.vendor
|
return self.vendor
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return self._repr()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Vendor"
|
verbose_name = "Vendor"
|
||||||
verbose_name_plural = "Vendors"
|
verbose_name_plural = "Vendors"
|
||||||
ordering = ["-id"]
|
ordering = ["-id"]
|
||||||
|
|
||||||
|
class VendorsAddresses(models.Model):
|
||||||
|
id = models.BigAutoField(primary_key=True)
|
||||||
|
vendor = models.ForeignKey(VendorsData, default=None, on_delete=models.CASCADE)
|
||||||
|
vendorid = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
|
||||||
|
vendorUUID = models.UUIDField()
|
||||||
|
coin = models.CharField(max_length=10)
|
||||||
|
address = models.CharField(max_length=250)
|
||||||
|
addrAddDate = models.DateTimeField(auto_now_add=True)
|
||||||
|
addrDeleted = models.BooleanField(default=False)
|
||||||
|
addrUpdated = models.DateTimeField(auto_now=True)
|
||||||
|
addrIsActive = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.address
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Address"
|
||||||
|
verbose_name_plural = "Addresses"
|
||||||
|
ordering = ["-addrAddDate"]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,321 +7,349 @@
|
||||||
<!-- ============================================================== -->
|
<!-- ============================================================== -->
|
||||||
<!-- Main content -->
|
<!-- Main content -->
|
||||||
<div class="h-screen flex-grow-1 overflow-y-lg-auto">
|
<div class="h-screen flex-grow-1 overflow-y-lg-auto">
|
||||||
<!-- Header -->
|
|
||||||
<header class="bg-surface-primary border-bottom pt-6">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="mb-npx">
|
|
||||||
<div class="row align-items-center">
|
|
||||||
<div class="col-sm-6 col-12 mb-4 mb-sm-0">
|
|
||||||
<!-- Title -->
|
|
||||||
<h1 class="h2 mb-0 ls-tight"> Dashboard </h1>
|
|
||||||
<hr class="navbar-divider my-1 opacity-20">
|
|
||||||
<p> Hello "user", welcome! Here's what's happening with your store today.</p>
|
|
||||||
</div>
|
|
||||||
<!-- Actions -->
|
|
||||||
<hr class="navbar-divider my-1 opacity-20">
|
|
||||||
</div>
|
|
||||||
<!-- Nav -->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<!-- Main -->
|
<!-- Main -->
|
||||||
<main class="py-6 bg-surface-secondary">
|
<div class="flex-fill overflow-y-lg-auto scrollbar bg-surface-primary rounded-top-4 rounded-top-start-lg-4 rounded-top-end-lg-0 border-top border-lg shadow">
|
||||||
<div class="container-fluid">
|
<main class="container-fluid px-3 py-5 p-lg-6 p-xxl-10">
|
||||||
<!-- Card stats -->
|
<div class="mb-6 mb-xl-10">
|
||||||
<div class="row g-6 mb-6">
|
<div class="row g-3 align-items-center">
|
||||||
<div class="col-xl-3 col-sm-6 col-12">
|
|
||||||
<div class="card shadow border-0">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Earnings</span>
|
<h1 class="h2 ls-tight">Dashboard</h1>
|
||||||
<span class="h3 font-bold mb-0">$750.90</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
|
||||||
<div class="icon icon-shape bg-tertiary text-white text-lg rounded-circle">
|
</div>
|
||||||
<i class="bi bi-credit-card"></i>
|
</div>
|
||||||
|
|
||||||
|
<div class="row g-3 g-xxl-6">
|
||||||
|
<div class="col-xxl-12">
|
||||||
|
<div class="vstack gap-3 gap-md-6">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md col-sm-6">
|
||||||
|
<div class="card bg-success bg-opacity-10 border-success border-opacity-40">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<img class="w-5 flex-none" src="{% static '/img/crypto/color/usdt.svg' %}" alt="..." />
|
||||||
|
<h6>USDT</h6>
|
||||||
|
</div>
|
||||||
|
<div class="text-xs mt-4">
|
||||||
|
Total balance
|
||||||
|
</div>
|
||||||
|
<div class="text-sm font-semibold">
|
||||||
|
75.800,00 USDT
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2 mb-0 text-sm">
|
</div>
|
||||||
<span class="badge badge-pill bg-soft-success text-success me-2">
|
<div class="col-md col-sm-6">
|
||||||
<i class="bi bi-arrow-up me-1"></i>13%
|
<div class="card border-primary-hover">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<div class="d-flex align-items-center gap-2">
|
||||||
|
<img class="w-5 flex-none" src="{% static '/img/crypto/color/btc.svg' %}" alt="..." />
|
||||||
|
<a href="/pages/exchange-details.html" class="h6 stretched-link">BTC</a>
|
||||||
|
</div>
|
||||||
|
<div class="text-sm font-semibold mt-3">
|
||||||
|
3.2893 USDT
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center gap-2 mt-1 text-xs">
|
||||||
|
<span class="badge badge-xs rounded-pill text-bg-success">
|
||||||
|
<i class="bi bi-arrow-up-right"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-nowrap text-xs text-muted">Since last month</span>
|
<span>+13.7%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xl-3 col-sm-6 col-12">
|
<div class="col-md col-sm-6">
|
||||||
<div class="card shadow border-0">
|
<div class="card border-primary-hover">
|
||||||
<div class="card-body">
|
<div class="card-body p-4">
|
||||||
<div class="row">
|
<div class="d-flex align-items-center gap-2">
|
||||||
<div class="col">
|
<img class="w-5 flex-none" src="{% static '/img/crypto/color/ada.svg' %}" alt="..." />
|
||||||
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Balance</span>
|
<a href="/pages/exchange-details.html" class="h6 stretched-link">ADA</a>
|
||||||
<span class="h3 font-bold mb-0">215</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<div class="text-sm font-semibold mt-3">
|
||||||
<div class="icon icon-shape bg-primary text-white text-lg rounded-circle">
|
10.745,49 ADA
|
||||||
<i class="bi bi-people"></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="d-flex align-items-center gap-2 mt-1 text-xs">
|
||||||
</div>
|
<span class="badge badge-xs rounded-pill text-bg-danger">
|
||||||
<div class="mt-2 mb-0 text-sm">
|
<i class="bi bi-arrow-up-right"></i>
|
||||||
<span class="badge badge-pill bg-soft-success text-success me-2">
|
|
||||||
<i class="bi bi-arrow-up me-1"></i>30%
|
|
||||||
</span>
|
</span>
|
||||||
<span class="text-nowrap text-xs text-muted">Since last month</span>
|
<span>-3.2%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xl-3 col-sm-6 col-12">
|
<div class="col-md col-sm-6">
|
||||||
<div class="card shadow border-0">
|
<div class="card border-primary-hover">
|
||||||
<div class="card-body">
|
<div class="card-body p-4">
|
||||||
<div class="row">
|
<div class="d-flex align-items-center gap-2">
|
||||||
<div class="col">
|
<img class="w-5 flex-none" src="{% static '/img/crypto/color/eos.svg' %}" alt="..." />
|
||||||
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Customers</span>
|
<a href="/pages/exchange-details.html" class="h6 stretched-link">EOS</a>
|
||||||
<span class="h3 font-bold mb-0">1.400</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
<div class="text-sm font-semibold mt-3">
|
||||||
<div class="icon icon-shape bg-info text-white text-lg rounded-circle">
|
7.890,00 EOS
|
||||||
<i class="bi bi-clock-history"></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="d-flex align-items-center gap-2 mt-1 text-xs">
|
||||||
</div>
|
<span class="badge badge-xs rounded-pill text-bg-danger">
|
||||||
<div class="mt-2 mb-0 text-sm">
|
<i class="bi bi-arrow-up-right"></i>
|
||||||
<span class="badge badge-pill bg-soft-danger text-danger me-2">
|
|
||||||
<i class="bi bi-arrow-down me-1"></i>-5%
|
|
||||||
</span>
|
</span>
|
||||||
<span class="text-nowrap text-xs text-muted">Since last month</span>
|
<span>-2.2%</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xl-3 col-sm-6 col-12">
|
<div class="col-md-1 d-none d-md-block">
|
||||||
<div class="card shadow border-0">
|
<div class="card h-md-full d-flex flex-column align-items-center justify-content-center py-4 bg-light bg-opacity-80 bg-opacity-100-hover">
|
||||||
<div class="card-body">
|
<a href="#cryptoModal" class="stretched-link text-dark" data-bs-toggle="modal">
|
||||||
<div class="row">
|
<i class="bi bi-plus-lg"></i>
|
||||||
<div class="col">
|
</a>
|
||||||
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Invoices</span>
|
|
||||||
<span class="h3 font-bold mb-0">150</span>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
|
||||||
<div class="icon icon-shape bg-warning text-white text-lg rounded-circle">
|
|
||||||
<i class="bi bi-minecart-loaded"></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2 mb-0 text-sm">
|
<div class="card">
|
||||||
<span class="badge badge-pill bg-soft-success text-success me-2">
|
<div class="card-body pb-0">
|
||||||
<i class="bi bi-arrow-up me-1"></i>10%
|
<div class="d-flex justify-content-between align-items-center">
|
||||||
|
<div>
|
||||||
|
<h5>Transaction History</h5>
|
||||||
|
</div>
|
||||||
|
<div class="hstack align-items-center">
|
||||||
|
<a href="#" class="text-muted">
|
||||||
|
<i class="bi bi-arrow-repeat"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-group list-group-flush">
|
||||||
|
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||||
|
<i class="bi bi-send-fill"></i>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<span class="d-block text-heading text-sm font-semibold">
|
||||||
|
Bitcoin
|
||||||
</span>
|
</span>
|
||||||
<span class="text-nowrap text-xs text-muted">Since last month</span>
|
<span class="d-none d-sm-block text-muted text-xs">
|
||||||
|
2 days ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block text-sm">
|
||||||
|
0xd029384sd343fd...eq23
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block">
|
||||||
|
<span class="badge bg-light text-warning rounded-pill">
|
||||||
|
Pending
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<span class="d-block text-heading text-sm font-bold">
|
||||||
|
+0.2948 BTC
|
||||||
|
</span>
|
||||||
|
<span class="d-block text-muted text-xs">
|
||||||
|
+$10,930.90
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||||
|
<i class="bi bi-send-fill"></i>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<span class="d-block text-heading text-sm font-semibold">
|
||||||
|
Cardano
|
||||||
|
</span>
|
||||||
|
<span class="d-none d-sm-block text-muted text-xs">
|
||||||
|
2 days ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block text-sm">
|
||||||
|
0xd029384sd343fd...eq23
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block">
|
||||||
|
<span class="badge bg-light text-danger rounded-pill">
|
||||||
|
Canceled
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<span class="d-block text-heading text-sm font-bold">
|
||||||
|
+0.2948 BTC
|
||||||
|
</span>
|
||||||
|
<span class="d-block text-muted text-xs">
|
||||||
|
+$10,930.90
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||||
|
<i class="bi bi-send-fill"></i>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<span class="d-block text-heading text-sm font-semibold">
|
||||||
|
Binance
|
||||||
|
</span>
|
||||||
|
<span class="d-none d-sm-block text-muted text-xs">
|
||||||
|
2 days ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block text-sm">
|
||||||
|
0xd029384sd343fd...eq23
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block">
|
||||||
|
<span class="badge bg-light text-success rounded-pill">
|
||||||
|
Successful
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<span class="d-block text-heading text-sm font-bold">
|
||||||
|
+0.2948 BTC
|
||||||
|
</span>
|
||||||
|
<span class="d-block text-muted text-xs">
|
||||||
|
+$10,930.90
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||||
|
<i class="bi bi-send-fill"></i>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<span class="d-block text-heading text-sm font-semibold">
|
||||||
|
Bitcoin
|
||||||
|
</span>
|
||||||
|
<span class="d-none d-sm-block text-muted text-xs">
|
||||||
|
2 days ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block text-sm">
|
||||||
|
0xd029384sd343fd...eq23
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block">
|
||||||
|
<span class="badge bg-light text-warning rounded-pill">
|
||||||
|
Pending
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<span class="d-block text-heading text-sm font-bold">
|
||||||
|
+0.2948 BTC
|
||||||
|
</span>
|
||||||
|
<span class="d-block text-muted text-xs">
|
||||||
|
+$10,930.90
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||||
|
<i class="bi bi-send-fill"></i>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<span class="d-block text-heading text-sm font-semibold">
|
||||||
|
Bitcoin
|
||||||
|
</span>
|
||||||
|
<span class="d-none d-sm-block text-muted text-xs">
|
||||||
|
2 days ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block text-sm">
|
||||||
|
0xd029384sd343fd...eq23
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block">
|
||||||
|
<span class="badge bg-light text-danger rounded-pill">
|
||||||
|
Canceled
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<span class="d-block text-heading text-sm font-bold">
|
||||||
|
+0.2948 BTC
|
||||||
|
</span>
|
||||||
|
<span class="d-block text-muted text-xs">
|
||||||
|
+$10,930.90
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||||
|
<i class="bi bi-send-fill"></i>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<span class="d-block text-heading text-sm font-semibold">
|
||||||
|
Bitcoin
|
||||||
|
</span>
|
||||||
|
<span class="d-none d-sm-block text-muted text-xs">
|
||||||
|
2 days ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block text-sm">
|
||||||
|
0xd029384sd343fd...eq23
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block">
|
||||||
|
<span class="badge bg-light text-success rounded-pill">
|
||||||
|
Successful
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<span class="d-block text-heading text-sm font-bold">
|
||||||
|
+0.2948 BTC
|
||||||
|
</span>
|
||||||
|
<span class="d-block text-muted text-xs">
|
||||||
|
+$10,930.90
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
|
||||||
|
<i class="bi bi-send-fill"></i>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<span class="d-block text-heading text-sm font-semibold">
|
||||||
|
Bitcoin
|
||||||
|
</span>
|
||||||
|
<span class="d-none d-sm-block text-muted text-xs">
|
||||||
|
2 days ago
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block text-sm">
|
||||||
|
0xd029384sd343fd...eq23
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block">
|
||||||
|
<span class="badge bg-light text-success rounded-pill">
|
||||||
|
Successful
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
<span class="d-block text-heading text-sm font-bold">
|
||||||
|
+0.2948 BTC
|
||||||
|
</span>
|
||||||
|
<span class="d-block text-muted text-xs">
|
||||||
|
+$10,930.90
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="card shadow border-0 mb-7">
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="mb-0">Last 5 paid invoices</h5>
|
|
||||||
</div>
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-hover table-nowrap">
|
|
||||||
<thead class="thead-light">
|
|
||||||
<tr>
|
|
||||||
<th scope="col">Name</th>
|
|
||||||
<th scope="col">Date</th>
|
|
||||||
<th scope="col">Company</th>
|
|
||||||
<th scope="col">Offer</th>
|
|
||||||
<th scope="col">Meeting</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
Robert Fox
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
Feb 15, 2021
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-1.png" class="avatar avatar-xs rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
Dribbble
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
$3.500
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge badge-lg badge-dot">
|
|
||||||
<i class="bg-success"></i>Scheduled
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td class="text-end">
|
|
||||||
<a href="#" class="btn btn-sm btn-neutral">View</a>
|
|
||||||
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
|
|
||||||
<i class="bi bi-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://images.unsplash.com/photo-1610271340738-726e199f0258?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
Darlene Robertson
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
Apr 15, 2021
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-2.png" class="avatar avatar-xs rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
Netguru
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
$2.750
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge badge-lg badge-dot">
|
|
||||||
<i class="bg-warning"></i>Postponed
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td class="text-end">
|
|
||||||
<a href="#" class="btn btn-sm btn-neutral">View</a>
|
|
||||||
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
|
|
||||||
<i class="bi bi-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://images.unsplash.com/photo-1610878722345-79c5eaf6a48c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
Theresa Webb
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
Mar 20, 2021
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-3.png" class="avatar avatar-xs rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
Figma
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
$4.200
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge badge-lg badge-dot">
|
|
||||||
<i class="bg-success"></i>Scheduled
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td class="text-end">
|
|
||||||
<a href="#" class="btn btn-sm btn-neutral">View</a>
|
|
||||||
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
|
|
||||||
<i class="bi bi-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://images.unsplash.com/photo-1612422656768-d5e4ec31fac0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
Kristin Watson
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
Feb 15, 2021
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-4.png" class="avatar avatar-xs rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
Mailchimp
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
$3.500
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge badge-lg badge-dot">
|
|
||||||
<i class="bg-dark"></i>Not discussed
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td class="text-end">
|
|
||||||
<a href="#" class="btn btn-sm btn-neutral">View</a>
|
|
||||||
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
|
|
||||||
<i class="bi bi-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://images.unsplash.com/photo-1608976328267-e673d3ec06ce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
Cody Fisher
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
Apr 10, 2021
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-5.png" class="avatar avatar-xs rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
Webpixels
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
$1.500
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge badge-lg badge-dot">
|
|
||||||
<i class="bg-danger"></i>Canceled
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td class="text-end">
|
|
||||||
<a href="#" class="btn btn-sm btn-neutral">View</a>
|
|
||||||
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
|
|
||||||
<i class="bi bi-trash"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0 py-5">
|
|
||||||
<span class="text-muted text-sm">#</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Dashboard -->
|
<!-- Dashboard -->
|
||||||
<!-- End Page-content -->
|
<!-- End Page-content -->
|
||||||
<div>
|
<div>
|
||||||
{% block footer %}
|
|
||||||
{% include "partials/footer.html" %}
|
|
||||||
{% endblock footer %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- end main content-->
|
<!-- end main content-->
|
||||||
{% endblock content %}
|
{% endblock content %}
|
|
@ -1,6 +1,6 @@
|
||||||
{% extends "partials/base.html" %}
|
{% extends "partials/base.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block title %}Dashboard{% endblock title %}
|
{% block title %}Vendor Page{% endblock title %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- ============================================================== -->
|
<!-- ============================================================== -->
|
||||||
<!-- Start right Content here -->
|
<!-- Start right Content here -->
|
||||||
|
@ -8,24 +8,7 @@
|
||||||
<!-- Main content -->
|
<!-- Main content -->
|
||||||
<div class="h-screen flex-grow-1 overflow-y-lg-auto">
|
<div class="h-screen flex-grow-1 overflow-y-lg-auto">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<header class="bg-surface-primary border-bottom pt-6">
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="mb-npx">
|
|
||||||
<div class="row align-items-center">
|
|
||||||
<div class="col-sm-6 col-12 mb-4 mb-sm-0">
|
|
||||||
<!-- Title -->
|
|
||||||
<h1 class="h2 mb-0 ls-tight"> Vendor Page </h1>
|
|
||||||
<hr class="navbar-divider my-1 opacity-20">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<!-- Actions -->
|
|
||||||
<hr class="navbar-divider my-1 opacity-20">
|
|
||||||
</div>
|
|
||||||
<!-- Nav -->
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
<!-- Main -->
|
<!-- Main -->
|
||||||
<main class="py-6 bg-surface-secondary">
|
<main class="py-6 bg-surface-secondary">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
|
@ -39,9 +22,9 @@
|
||||||
<thead class="thead-light">
|
<thead class="thead-light">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">VendorID</th>
|
<th scope="col">VendorID</th>
|
||||||
<th scope="col">Creation Date</th>
|
|
||||||
<th scope="col">Website Name</th>
|
<th scope="col">Website Name</th>
|
||||||
<th scope="col"></th>
|
<th scope="col">Created</th>
|
||||||
|
<th scope="col">Updated</th>
|
||||||
<th scope="col">Status</th>
|
<th scope="col">Status</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -50,22 +33,18 @@
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-1.png" class="avatar avatar-xs rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
{{vendor.vendor}}
|
{{vendor.vendor}}
|
||||||
</a>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{vendor.vendorAddDate}}
|
{{vendor.vendorWebName|truncatechars:20}}
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-1.png" class="avatar avatar-xs rounded-circle me-2">
|
|
||||||
<a class="text-heading font-semibold" href="#">
|
|
||||||
{{vendor.vendorWebName}}
|
|
||||||
</a>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
|
{{vendor.vendorAddDate}}
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{vendor.vendorUpdated|timesince}} ago
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if vendor.vendorIsActive == 0 %}
|
{% if vendor.vendorIsActive == 0 %}
|
||||||
|
@ -79,8 +58,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end">
|
<td class="text-end">
|
||||||
<a href="{{vendor.vendorUUID}}" class="btn btn-sm btn-neutral">Edit</a>
|
<a href="{% url 'edit-vendor' vendor.vendorUUID %}" class="btn d-inline-flex btn-sm btn-neutral text-neutral"><span><i class="bi bi-gear me-1"></i></span> <span class="d-none d-sm-block me-2">Manage</span> </a>
|
||||||
<a href="addresses/{{vendor.vendorUUID}}" class="btn btn-sm btn-neutral">Addresses</a>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
@ -90,7 +68,7 @@
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer border-0 py-5">
|
<div class="card-footer border-0 py-5">
|
||||||
<span class="text-muted text-sm">#</span>
|
<div class="text-center"> <a href="{% url 'create-vendor' %}" class="btn btn-sm btn-primary">Create</a> </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -99,13 +77,5 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- Dashboard -->
|
<!-- Dashboard -->
|
||||||
<!-- End Page-content -->
|
<!-- End Page-content -->
|
||||||
<div>
|
|
||||||
{% block footer %}
|
|
||||||
{% include "partials/footer.html" %}
|
|
||||||
{% endblock footer %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- end main content-->
|
<!-- end main content-->
|
||||||
{% endblock content %}
|
{% endblock content %}
|
|
@ -0,0 +1,184 @@
|
||||||
|
{% extends "partials/base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% block title %}Vendor Edit Page{% endblock title %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ============================================================== -->
|
||||||
|
<!-- Start right Content here -->
|
||||||
|
<!-- ============================================================== -->
|
||||||
|
<!-- Main content -->
|
||||||
|
|
||||||
|
|
||||||
|
<div class="flex-fill overflow-y-lg-auto scrollbar bg-surface-primary rounded-top-4 rounded-top-start-lg-4 rounded-top-end-lg-0 border-top border-lg shadow">
|
||||||
|
<main class="container-fluid px-3 py-5 p-lg-6 p-xxl-10">
|
||||||
|
<header class="border-bottom mb-10">
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-sm-6 col-12">
|
||||||
|
<h1 class="h2 ls-tight">
|
||||||
|
{% block vendor_title %} {{vendor_title}} {% endblock vendor_title %}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul class="nav nav-tabs overflow-x border-0 mt-4">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="{% url 'edit-vendor' vendors.vendorUUID %}" class="nav-link">Options</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="{% url 'add-address' vendors.vendorUUID %}" class="nav-link">Addresses</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/pages/account-billing.html" class="nav-link">Delete</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
|
||||||
|
<form method="POST" action="">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{{ form.non_field_errors }}
|
||||||
|
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label for="{{ form.vendorWebName.id_for_label}}" class="form-label">Website Name <span><i class="bi bi-patch-question-fill text-danger" title="Shown on invoice page"></i></span></label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8 col-xl-5">
|
||||||
|
<div class="">
|
||||||
|
<div class="input-group position-relative">
|
||||||
|
<span class="input-group-text"><i class="bi bi-three-dots"></i></span>
|
||||||
|
<input id="vendorWebName" name="vendorWebName" type="text" class="form-control" {% if not vendors.vendorWebName %} placeholder="Website Name" {% else %} value="{{vendors.vendorWebName}}" {% endif %} aria-label="vendorWebName">
|
||||||
|
</div>
|
||||||
|
<span class="mt-2 valid-feedback">Looks good!</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="my-6" />
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label for="{{ form.vendorWebAddr.id_for_label}}" class="form-label">Website URL <span><i class="bi bi-patch-question-fill text-danger" title="Shown on invoice page"></i></span></label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8 col-xl-5">
|
||||||
|
<div class="">
|
||||||
|
<div class="input-group position-relative">
|
||||||
|
<span class="input-group-text"><i class="bi bi-window"></i></span>
|
||||||
|
<input id="vendorWebAddr" name="vendorWebAddr" type="text" class="form-control" {% if not vendors.vendorWebAddr %} placeholder="Website URL" {% else %} value="{{vendors.vendorWebAddr}}" {% endif %} aria-label="vendorWebAddr">
|
||||||
|
</div>
|
||||||
|
{% if form.vendorWebAddr.errors %}
|
||||||
|
{% for error in form.vendorWebAddr.errors %}
|
||||||
|
<span class="mt-2 valid-feedback">{{ error|escape }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="my-6" />
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label for="{{ form.vendorNetworkFee.id_for_label}}" class="form-label">Network Fee % <span><i class="bi bi-patch-question-fill text-danger" title="Max percentage "></i></span> </label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8 col-xl-2">
|
||||||
|
<select id="vendorNetworkFee" name="vendorNetworkFee" class="form-select" aria-describedby="vendorNetworkFeeHelpBlock">
|
||||||
|
<option value="0" {% if vendors.vendorNetworkFee == 0 %} selected="selected" {% endif %}>0%</option>
|
||||||
|
<option value="5" {% if vendors.vendorNetworkFee == 5 %} selected="selected" {% endif %}>5%</option>
|
||||||
|
<option value="10" {% if vendors.vendorNetworkFee == 10 %} selected="selected" {% endif %}>10%</option>
|
||||||
|
</select>
|
||||||
|
{% if form.vendorNetworkFee.errors %}
|
||||||
|
{% for error in form.vendorNetworkFee.errors %}
|
||||||
|
<span id="vendorNetworkFeeHelpBlock" class="text-danger">{{ error|escape }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="my-6" />
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label for="vendorPaidNotification" class="form-label">Notifications <span><i class="bi bi-patch-question-fill text-danger" title="Get notified by email for each payment received?"></i></span></label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8 col-xl-2">
|
||||||
|
<select id="vendorPaidNotification" name="vendorPaidNotification" class="form-select" aria-describedby="vendorPaidNotificationHelpBlock">
|
||||||
|
<option value="true" {% if vendors.vendorPaidNotification == True %} selected="selected" {% endif %}>Yes</option>
|
||||||
|
<option value="false" {% if vendors.vendorPaidNotification == False %} selected="selected" {% endif %}>No</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{% if form.vendorPaidNotification.errors %}
|
||||||
|
{% for error in form.vendorPaidNotification.errors %}
|
||||||
|
<span id="vendorPaidNotificationHelpBlock" class="text-danger">{{ error|escape }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<p class="my-6" />
|
||||||
|
|
||||||
|
<div class="row align-items-center">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label for="vendorCoverAmount" class="form-label">Cover Amount <span><i class="bi bi-patch-question-fill text-danger" title="Amount covered by you in case the buyer sends less than required"></i></span> </label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8 col-xl-2">
|
||||||
|
<select id="vendorCoverAmount" name="vendorCoverAmount" class="form-select" aria-describedby="vendorCoverAmountHelpBlock">
|
||||||
|
<option value="0" {% if vendors.vendorCoverAmount == 0 %} selected="selected" {% endif %}>0%</option>
|
||||||
|
<option value="1" {% if vendors.vendorCoverAmount == 1 %} selected="selected" {% endif %}>1%</option>
|
||||||
|
<option value="2" {% if vendors.vendorCoverAmount == 2 %} selected="selected" {% endif %}>2%</option>
|
||||||
|
<option value="3" {% if vendors.vendorCoverAmount == 3 %} selected="selected" {% endif %}>3%</option>
|
||||||
|
<option value="4" {% if vendors.vendorCoverAmount == 4 %} selected="selected" {% endif %}>4%</option>
|
||||||
|
<option value="5" {% if vendors.vendorCoverAmount == 5 %} selected="selected" {% endif %}>5%</option>
|
||||||
|
<option value="6" {% if vendors.vendorCoverAmount == 6 %} selected="selected" {% endif %}>6%</option>
|
||||||
|
<option value="7" {% if vendors.vendorCoverAmount == 7 %} selected="selected" {% endif %}>7%</option>
|
||||||
|
<option value="8" {% if vendors.vendorCoverAmount == 8 %} selected="selected" {% endif %}>8%</option>
|
||||||
|
<option value="9" {% if vendors.vendorCoverAmount == 9 %} selected="selected" {% endif %}>9%</option>
|
||||||
|
<option value="10" {% if vendors.vendorCoverAmount == 10 %} selected="selected" {% endif %}>10%</option>
|
||||||
|
</select>
|
||||||
|
{% if form.vendorCoverAmount.errors %}
|
||||||
|
{% for error in form.vendorCoverAmount.errors %}
|
||||||
|
<span id="vendorCoverAmountHelpBlock" class="text-danger">{{ error|escape }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="my-6" />
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-md-2">
|
||||||
|
<label for="vendorPayWindow" class="form-label">Payment <span><i class="bi bi-patch-question-fill text-danger" title="When should we send you your payments?"></i></span></label>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8 col-xl-2">
|
||||||
|
<select id="vendorPayWindow" name="vendorPayWindow" class="form-select" aria-describedby="vendorPayWindowHelpBlock">
|
||||||
|
<option value="true" {% if vendors.vendorPayWindow == 1 %} selected="selected" {% endif %}>Hourly</option>
|
||||||
|
<option value="false" {% if vendors.vendorPayWindow == 0 %} selected="selected" {% endif %}>Daily</option>
|
||||||
|
</select>
|
||||||
|
{% if form.vendorPayWindow.errors %}
|
||||||
|
{% for error in form.vendorPayWindow.errors %}
|
||||||
|
<span id="vendorPayWindowHelpBlock" class="text-danger">{{ error|escape }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="my-6" />
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="offset-4 col-8">
|
||||||
|
<input name="submit" type="submit" class="btn btn-primary" value="Submit" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if messages %}
|
||||||
|
<ul class="messages text-center">
|
||||||
|
{% for message in messages %}
|
||||||
|
<p{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</p>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Dashboard -->
|
||||||
|
<!-- End Page-content -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- end main content-->
|
||||||
|
{% endblock content %}
|
|
@ -6,6 +6,13 @@ urlpatterns = [
|
||||||
path('login/', views.LoginPage, name="login"),
|
path('login/', views.LoginPage, name="login"),
|
||||||
path('logout/', views.LogoutPage, name="logout"),
|
path('logout/', views.LogoutPage, name="logout"),
|
||||||
|
|
||||||
|
|
||||||
path('', views.index, name="dashboard"),
|
path('', views.index, name="dashboard"),
|
||||||
path('vendor/', views.VendorPage, name="vendor"),
|
path('vendor/', views.VendorPage, name="vendor"),
|
||||||
|
path('vendor/edit/<str:vdr>/', views.VendorEditPage, name="edit-vendor"),
|
||||||
|
path('vendor/create', views.VendorCreatePage, name="create-vendor"),
|
||||||
|
path('vendor/address/<str:vdr>/', views.VendorAddrPage, name="add-address"),
|
||||||
|
|
||||||
|
|
||||||
|
#path('vendor-success', views.VendorSuccess, name="vendor-success")
|
||||||
]
|
]
|
148
base/views.py
148
base/views.py
|
@ -1,13 +1,21 @@
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from users.models import User
|
|
||||||
from .models import VendorsData
|
|
||||||
from django.contrib.auth import authenticate, login, logout
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.db.models import Q
|
||||||
|
from django_otp.decorators import otp_required
|
||||||
|
#
|
||||||
|
from users.models import User
|
||||||
|
from .models import VendorsData, CryptoCoins, VendorsAddresses
|
||||||
|
from .forms import VendorEditForm, VendorAddrAddForm
|
||||||
|
from .functions import vendor_generator, checksumCheck
|
||||||
|
import uuid, hashlib
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
@login_required(login_url='login')
|
@login_required(login_url='login')
|
||||||
|
#@otp_required()
|
||||||
def index(request):
|
def index(request):
|
||||||
context = {}
|
context = {}
|
||||||
return render(request, 'base/dashboard.html', context)
|
return render(request, 'base/dashboard.html', context)
|
||||||
|
@ -33,7 +41,7 @@ def LoginPage(request):
|
||||||
|
|
||||||
|
|
||||||
context = {}
|
context = {}
|
||||||
return render(request, 'base/login.html', context)
|
return render(request, 'base/loogin2.html', context)
|
||||||
|
|
||||||
def LogoutPage(request):
|
def LogoutPage(request):
|
||||||
logout(request)
|
logout(request)
|
||||||
|
@ -41,6 +49,136 @@ def LogoutPage(request):
|
||||||
|
|
||||||
@login_required(login_url='login')
|
@login_required(login_url='login')
|
||||||
def VendorPage(request):
|
def VendorPage(request):
|
||||||
vendors = VendorsData.objects.filter(vendorid_id=request.user.id)
|
vendors = VendorsData.objects.filter(Q(vendorid_id=request.user.id) & Q(vendorDeleted=False))
|
||||||
context = {'vendors': vendors}
|
context = {'vendors': vendors}
|
||||||
return render(request, 'base/vendor.html', context)
|
return render(request, 'base/vendor.html', context)
|
||||||
|
|
||||||
|
@login_required(login_url='login')
|
||||||
|
#@otp_required(login_url='two_factor:login')
|
||||||
|
def VendorEditPage(request, vdr ):
|
||||||
|
try:
|
||||||
|
vendors = VendorsData.objects.get(Q(vendorid_id=request.user.id) & Q(vendorUUID=vdr))
|
||||||
|
form = VendorEditForm()
|
||||||
|
vendor_title = "Edit Vendor"
|
||||||
|
if request.method == 'POST':
|
||||||
|
if request.POST.get('submit') == 'Submit':
|
||||||
|
try:
|
||||||
|
vendors = VendorsData.objects.get(Q(vendorid_id=request.user.id) & Q(vendorUUID=vdr))
|
||||||
|
form = VendorEditForm(request.POST, instance=vendors)
|
||||||
|
if form.is_valid():
|
||||||
|
form.save()
|
||||||
|
messages.success(request, "Vendor information saved")
|
||||||
|
return redirect('vendor')
|
||||||
|
else:
|
||||||
|
messages.error(request, "Something wrong happened, try again.")
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, "Something wrong happened, try again.")
|
||||||
|
context = {'vendors': vendors, 'form': form, 'vendor_title': vendor_title}
|
||||||
|
return render(request, 'base/vendorEdit.html', context)
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, e)
|
||||||
|
return redirect('vendor')
|
||||||
|
|
||||||
|
@login_required(login_url='login')
|
||||||
|
def VendorCreatePage(request):
|
||||||
|
vendor_title = "Create Vendor"
|
||||||
|
context = {'vendor_title': vendor_title}
|
||||||
|
if request.method == 'POST':
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
vendor_accounts = len(VendorsData.objects.filter(vendorid_id=request.user.id))
|
||||||
|
if vendor_accounts > request.user.vendornr:
|
||||||
|
messages.error(request, "You have reached the maximum number of vendor accounts")
|
||||||
|
return redirect('vendor')
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
form = VendorEditForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
# Generate vendor
|
||||||
|
new_vendor = form.save(commit=False)
|
||||||
|
try:
|
||||||
|
vdr = vendor_generator()
|
||||||
|
vendor_check = VendorsData.objects.get(vendorUUID=vdr)
|
||||||
|
while vendor_check is vdr:
|
||||||
|
vdr = vendor_generator()
|
||||||
|
vendor_check = VendorsData.objects.get(vendorUUID=vdr)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
secretKey = str(uuid.uuid4())
|
||||||
|
md5s = hashlib.md5(secretKey.encode()).hexdigest()
|
||||||
|
new_vendor.vendor = vdr
|
||||||
|
new_vendor.vendorSecretKey = md5s
|
||||||
|
new_vendor.vendorSkipScreen = False
|
||||||
|
new_vendor.vendorUUID = uuid.uuid4()
|
||||||
|
new_vendor.vendorid_id = request.user.id
|
||||||
|
# Save vendor
|
||||||
|
try:
|
||||||
|
new_vendor.save()
|
||||||
|
vendor_title = "Congrats!"
|
||||||
|
context = {'vendor_title': vendor_title, "vendor": new_vendor, 'secret': secretKey}
|
||||||
|
return render(request, 'base/vendorSuccess.html', context)
|
||||||
|
except Exception:
|
||||||
|
messages.error(request, "Unable to create vendor") #
|
||||||
|
else:
|
||||||
|
messages.error(request, "The information submited is incomplete") #
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, "Something wrong happened, try again.")
|
||||||
|
return render(request, 'base/vendorCreate.html', context)
|
||||||
|
|
||||||
|
@login_required(login_url='login')
|
||||||
|
def VendorAddrPage(request,vdr):
|
||||||
|
try:
|
||||||
|
coins = CryptoCoins.objects.filter(coinIsActive=True)
|
||||||
|
try:
|
||||||
|
vendors = VendorsData.objects.get(Q(vendorid_id=request.user.id) & Q(vendorUUID=vdr))
|
||||||
|
except:
|
||||||
|
messages.error(request, "Vendor not found")
|
||||||
|
return redirect('vendor')
|
||||||
|
addresses = VendorsAddresses.objects.filter(Q(vendorUUID=vdr) & Q(vendorid_id=request.user.id))
|
||||||
|
form = VendorAddrAddForm()
|
||||||
|
vendor_title = "Add a crypto address"
|
||||||
|
if request.method == 'POST':
|
||||||
|
if request.POST.get('submit') == 'Submit':
|
||||||
|
try:
|
||||||
|
if checksumCheck(request.POST.get('coin').lower(), request.POST.get('address')) == False:
|
||||||
|
messages.error(request, "Invalid address")
|
||||||
|
context = {'vendor_title': vendor_title, 'coins': coins, 'vendors': vendors, "addresses": addresses}
|
||||||
|
return render(request, 'base/vendorAddr.html', context)
|
||||||
|
except:
|
||||||
|
# stop here, return nothing, notify us.
|
||||||
|
return redirect('vendorAddr', vdr)
|
||||||
|
##check if duplicate
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
form = VendorAddrAddForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
new_address = form.save(commit=False)
|
||||||
|
new_address.vendorUUID = vdr
|
||||||
|
new_address.addrDeleted = False
|
||||||
|
new_address.addrIsActive = True
|
||||||
|
new_address.vendor_id = vendors.id
|
||||||
|
new_address.vendorid_id = request.user.id
|
||||||
|
try:
|
||||||
|
for obj in VendorsAddresses.objects.filter(Q(coin=request.POST.get('coin')) & Q(addrIsActive=True) & Q(vendorid_id=request.user.id)):
|
||||||
|
obj.addrIsActive = False
|
||||||
|
obj.save()
|
||||||
|
new_address.save()
|
||||||
|
messages.success(request, "Vendor address saved")
|
||||||
|
#add email notification
|
||||||
|
context = {'vendor_title': vendor_title, 'coins': coins, 'vendors': vendors, "addresses": addresses}
|
||||||
|
return render(request, 'base/vendorAddr.html', context)
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, e) #
|
||||||
|
else:
|
||||||
|
messages.error(request, "Something wrong happened, try again!")
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, "Something wrong happened, try again!")
|
||||||
|
|
||||||
|
context = {'vendor_title': vendor_title, 'coins': coins, 'vendors': vendors, "addresses": addresses}
|
||||||
|
return render(request, 'base/vendorAddr.html', context)
|
||||||
|
except Exception as e:
|
||||||
|
messages.error(request, "Something wrong happened, try again!")
|
||||||
|
return redirect('vendor')
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ SECRET_KEY = 'django-insecure-z#_ppfgs06)e4v18t!970-&-&jkxht!tw&ms#u5n_m-fbxulwa
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = ['10.0.70.5', '127.0.0.1']
|
||||||
|
|
||||||
# User substitution
|
# User substitution
|
||||||
# https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#auth-custom-user
|
# https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#auth-custom-user
|
||||||
|
@ -43,6 +43,13 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'django_bcrypt',
|
'django_bcrypt',
|
||||||
|
'django_otp',
|
||||||
|
'django_otp.plugins.otp_static',
|
||||||
|
'django_otp.plugins.otp_totp',
|
||||||
|
'django_otp.plugins.otp_email', # <- if you want email capability.
|
||||||
|
'two_factor',
|
||||||
|
'two_factor.plugins.phonenumber', # <- if you want phone number capability.
|
||||||
|
'two_factor.plugins.email', # <- if you want email capability.
|
||||||
#
|
#
|
||||||
'base.apps.BaseConfig',
|
'base.apps.BaseConfig',
|
||||||
'users.apps.UsersConfig',
|
'users.apps.UsersConfig',
|
||||||
|
@ -54,6 +61,7 @@ MIDDLEWARE = [
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
'django_otp.middleware.OTPMiddleware',
|
||||||
'django.contrib.messages.middleware.MessageMiddleware',
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
]
|
]
|
||||||
|
@ -113,13 +121,13 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
'''
|
||||||
PASSWORD_HASHERS = [
|
PASSWORD_HASHERS = [
|
||||||
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
|
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
|
||||||
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
|
||||||
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
|
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
|
||||||
]
|
]
|
||||||
|
'''
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||||
|
@ -143,23 +151,44 @@ STATICFILES_DIRS = [
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
|
||||||
|
LOGGING = {
|
||||||
|
'version': 1,
|
||||||
|
'disable_existing_loggers': False,
|
||||||
|
'handlers': {
|
||||||
|
'console': {
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'loggers': {
|
||||||
|
'two_factor': {
|
||||||
|
'handlers': ['console'],
|
||||||
|
'level': 'INFO',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Messages customize
|
# Messages customize
|
||||||
|
|
||||||
MESSAGE_TAGS = {
|
MESSAGE_TAGS = {
|
||||||
messages.DEBUG: "alert-info",
|
messages.DEBUG: "text-info",
|
||||||
messages.INFO: "alert-info",
|
messages.INFO: "text-info",
|
||||||
messages.SUCCESS: "alert-success",
|
messages.SUCCESS: "text-success",
|
||||||
messages.WARNING: "alert-warning",
|
messages.WARNING: "text-warning",
|
||||||
messages.ERROR: "alert-danger",
|
messages.ERROR: "text-danger",
|
||||||
}
|
}
|
||||||
|
|
||||||
BCRYPT_ROUNDS = 11
|
BCRYPT_ROUNDS = 11
|
||||||
|
|
||||||
PASSWORD_RESET_TIMEOUT = 3600 #
|
PASSWORD_RESET_TIMEOUT = 3600 #
|
||||||
|
#LOGIN_URL = 'two_factor:login'
|
||||||
|
#OTP_LOGIN_URL = 'account:login'
|
||||||
|
|
||||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||||
SESSION_COOKIE_SECURE = False #to be switched to true when in prod
|
SESSION_COOKIE_SECURE = False #to be switched to true when in prod
|
||||||
CSRF_COOKIE_SECURE = False #to be switched to true when in prod
|
CSRF_COOKIE_SECURE = False #to be switched to true when in prod
|
||||||
SECURE_SSL_REDIRECT = False #to be switched to true when in prod
|
SECURE_SSL_REDIRECT = False #to be switched to true when in prod
|
||||||
SESSION_COOKIE_AGE = 3600
|
SESSION_COOKIE_AGE = 3600
|
||||||
|
TWO_FACTOR_REMEMBER_COOKIE_AGE = 3600
|
||||||
SERVER_EMAIL = 'info@litepay.ch'
|
SERVER_EMAIL = 'info@litepay.ch'
|
||||||
|
DEFAULT_FROM_EMAIL = 'info@litepay.ch'
|
|
@ -16,9 +16,15 @@ Including another URLconf
|
||||||
"""
|
"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
from two_factor.urls import urlpatterns as tf_urls
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('admin/', admin.site.urls),
|
#path('', include(tf_urls)),
|
||||||
path('', include('base.urls'))
|
path('', include('base.urls'))
|
||||||
]
|
]
|
||||||
|
|
||||||
|
'''
|
||||||
|
urlpatterns = [
|
||||||
|
path('admin/', admin.site.urls),
|
||||||
|
|
||||||
|
]
|
||||||
|
'''
|
Binary file not shown.
|
@ -18,9 +18,7 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- End Page-content -->
|
<!-- End Page-content -->
|
||||||
|
|
||||||
{% block footer %}
|
|
||||||
{% include "partials/footer.html" %}
|
|
||||||
{% endblock footer %}
|
|
||||||
</div>
|
</div>
|
||||||
<!-- end main content-->
|
<!-- end main content-->
|
||||||
{% endblock content %}
|
{% endblock content %}
|
|
@ -1,23 +1,27 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" data-theme="light">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="UTF-8">
|
||||||
<title>{% block title %}{% endblock title %} | </title>
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="color-scheme" content="dark light">
|
||||||
<meta content="Premium Multipurpose Admin & Dashboard Template" name="description" />
|
<title>{% block title %}{% endblock title %} |</title>
|
||||||
<meta content="Themesbrand" name="author" />
|
|
||||||
<!-- App favicon -->
|
<!-- App favicon -->
|
||||||
<link rel="shortcut icon" href="{% static 'images/favicon.ico'%}">
|
<link rel="shortcut icon" href="{% static 'images/favicon.ico'%}">
|
||||||
{% block css %}
|
{% block css %}
|
||||||
<!-- Bootstrap Css -->
|
<!-- Styles -->
|
||||||
<link href="https://unpkg.com/@webpixels/css@1.1.5/dist/index.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
|
<link rel="stylesheet" type="text/css" href="{% static '/css/main.css'%}">
|
||||||
<!-- Icons Css -->
|
<link rel="stylesheet" type="text/css" href="{% static '/css/utility.css'%}">
|
||||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.4.0/font/bootstrap-icons.min.css" rel="stylesheet" type="text/css" />
|
<!-- Bootstrap Icons -->
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css">
|
||||||
|
<!-- Fonts -->
|
||||||
|
<link rel="stylesheet" href="https://api.fontshare.com/v2/css?f=satoshi@900,700,500,300,401,400&display=swap">
|
||||||
{% endblock css %}
|
{% endblock css %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
|
||||||
|
<body class="bg-surface-secondary">
|
||||||
<!-- Begin page -->
|
<!-- Begin page -->
|
||||||
<div id="layout-wrapper">
|
<div id="layout-wrapper">
|
||||||
{% block header %}
|
{% block header %}
|
||||||
|
@ -29,8 +33,6 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% block pagetitle %}
|
{% block pagetitle %}
|
||||||
{% endblock pagetitle %}
|
{% endblock pagetitle %}
|
||||||
{% block footer %}
|
|
||||||
{% endblock footer %}
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
|
|
||||||
{% block footer %}
|
{% block footer %}
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6 text-center">
|
||||||
<script>document.write(new Date().getFullYear())</script> © Litepay.ch.
|
<script>document.write(new Date().getFullYear())</script> © Litepay.ch.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,90 +1,257 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block sidebar %}
|
{% block sidebar %}
|
||||||
<div class="d-flex flex-column flex-lg-row h-lg-full bg-surface-secondary">
|
<div class="d-flex flex-column flex-lg-row h-lg-full">
|
||||||
<!-- Vertical Navbar -->
|
<nav class="flex-none navbar navbar-vertical navbar-expand-lg navbar-light bg-transparent show h-lg-screen px-0 py-2 scrollbar" id="sidebar">
|
||||||
<nav class="navbar show navbar-vertical h-lg-screen navbar-expand-lg px-0 py-3 navbar-light bg-white border-bottom border-bottom-lg-0 border-end-lg" id="navbarVertical">
|
<div class="container-fluid px-3 px-md-4 px-lg-6">
|
||||||
<div class="container-fluid">
|
|
||||||
<!-- Toggler -->
|
|
||||||
<button class="navbar-toggler ms-n2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarCollapse" aria-controls="sidebarCollapse" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler ms-n2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarCollapse" aria-controls="sidebarCollapse" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<!-- Brand -->
|
<a class="navbar-brand d-inline-block py-lg-1 mb-lg-5" href="/pages/dashboard.html">
|
||||||
<a class="navbar-brand py-lg-2 mb-lg-5 px-lg-6 me-0" href="#">
|
<img src="https://ik.imagekit.io/litepaych/assets/new/img/logo_litepay_2020_blue.png" class="h-8 h-md-10" alt="...">
|
||||||
<img src="https://preview.webpixels.io/web/img/logos/clever-primary.svg" alt="...">
|
|
||||||
</a>
|
</a>
|
||||||
<!-- User menu (mobile) -->
|
|
||||||
<div class="navbar-user d-lg-none">
|
<div class="navbar-user d-lg-none">
|
||||||
<!-- Dropdown -->
|
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<!-- Toggle -->
|
<a class="d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="false" aria-expanded="false">
|
||||||
<a href="#" id="sidebarAvatar" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<div>
|
||||||
<div class="avatar-parent-child">
|
<div class="avatar avatar-sm text-bg-secondary rounded-circle">
|
||||||
<img alt="Image Placeholder" src="https://images.unsplash.com/photo-1548142813-c348350df52b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar- rounded-circle">
|
AE
|
||||||
<span class="avatar-child avatar-badge bg-success"></span>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-sm-block ms-3">
|
||||||
|
<span class="h6">Alexis</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block ms-md-2">
|
||||||
|
<i class="bi bi-chevron-down text-muted text-xs"></i>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<!-- Menu -->
|
<div class="dropdown-menu dropdown-menu-end">
|
||||||
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="sidebarAvatar">
|
<div class="dropdown-header">
|
||||||
<a href="#" class="dropdown-item">Profile</a>
|
<span class="d-block text-sm text-muted mb-1">Signed in as</span>
|
||||||
<a href="#" class="dropdown-item">Settings</a>
|
<span class="d-block text-heading font-semibold">Alexis Enache</span>
|
||||||
<a href="#" class="dropdown-item">Billing</a>
|
</div>
|
||||||
<hr class="dropdown-divider">
|
<div class="dropdown-divider"></div>
|
||||||
<a href="#" class="dropdown-item">Logout</a>
|
<a class="dropdown-item" href="#">
|
||||||
|
<i class="bi bi-house me-3"></i>Home
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="#">
|
||||||
|
<i class="bi bi-pencil me-3"></i>Edit profile
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item" href="#">
|
||||||
|
<i class="bi bi-gear me-3"></i>Settings
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="#">
|
||||||
|
<i class="bi bi-image me-3"></i>Media
|
||||||
|
</a>
|
||||||
|
<a class="dropdown-item" href="#">
|
||||||
|
<i class="bi bi-box-arrow-up me-3"></i>Share
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<a class="dropdown-item" href="#">
|
||||||
|
<i class="bi bi-person me-3"></i>Login
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Collapse -->
|
<div class="collapse navbar-collapse overflow-x-hidden" id="sidebarCollapse">
|
||||||
<div class="collapse navbar-collapse" id="sidebarCollapse">
|
|
||||||
<!-- Navigation -->
|
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item my-1">
|
||||||
|
<a class="nav-link d-flex align-items-center rounded-pill" href="/">
|
||||||
|
<i class="bi bi-houses-fill"></i>
|
||||||
|
<span>Dashboard</span>
|
||||||
|
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item my-1">
|
||||||
|
<a class="nav-link d-flex align-items-center rounded-pill" href="/pages/analytics.html">
|
||||||
|
<i class="bi bi-pie-chart-fill"></i>
|
||||||
|
<span>Analytics</span>
|
||||||
|
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item my-1">
|
||||||
|
<a class="nav-link d-flex align-items-center rounded-pill" href="/pages/wallet.html">
|
||||||
|
<i class="bi bi-wallet-fill"></i>
|
||||||
|
<span>Wallet</span>
|
||||||
|
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item my-1">
|
||||||
|
<a class="nav-link d-flex align-items-center rounded-pill" href="{% url 'vendor' %}">
|
||||||
|
<i class="bi bi-wallet-fill"></i>
|
||||||
|
<span>Vendor</span>
|
||||||
|
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item my-1">
|
||||||
|
<a class="nav-link d-flex align-items-center rounded-pill" href="#sidebar-exchange" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-exchange">
|
||||||
|
<i class="bi bi-bar-chart-fill"></i>
|
||||||
|
<span>Exchange</span>
|
||||||
|
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||||
|
</a>
|
||||||
|
<div class="collapse" id="sidebar-exchange">
|
||||||
|
<ul class="nav nav-sm flex-column mt-1">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#">
|
<a href="/pages/exchange-watchlist.html" class="nav-link">
|
||||||
<i class="bi bi-house"></i> Dashboard
|
Watchlist
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#">
|
<a href="/pages/exchange-history.html" class="nav-link">
|
||||||
<i class="bi bi-bar-chart"></i> Analitycs
|
Trade History
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#">
|
<a href="/pages/exchange-details.html" class="nav-link">
|
||||||
<i class="bi bi-chat"></i> Messages
|
Trade Details
|
||||||
<span class="badge bg-soft-primary text-primary rounded-pill d-inline-flex align-items-center ms-auto">6</span>
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="#">
|
<a href="/pages/exchange-create-form.html" class="nav-link">
|
||||||
<i class="bi bi-bookmarks"></i> Collections
|
Create Asset
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'vendor' %}">
|
<a href="/pages/exchange-staking.html" class="nav-link">
|
||||||
<i class="bi bi-people"></i> Vendor
|
Staking
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item my-1">
|
||||||
|
<a class="nav-link d-flex align-items-center rounded-pill" href="#sidebar-account" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-account">
|
||||||
|
<i class="bi bi-gear-fill"></i>
|
||||||
|
<span>Account</span>
|
||||||
|
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||||
|
</a>
|
||||||
|
<div class="collapse" id="sidebar-account">
|
||||||
|
<ul class="nav nav-sm flex-column mt-1">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/pages/account-general.html" class="nav-link">
|
||||||
|
Settings
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/pages/account-password.html" class="nav-link">
|
||||||
|
Password
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/pages/account-billing.html" class="nav-link">
|
||||||
|
Billing
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/pages/account-notifications.html" class="nav-link">
|
||||||
|
Notifications
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/pages/login.html" class="nav-link">
|
||||||
|
Login
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/pages/register.html" class="nav-link">
|
||||||
|
Register
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item my-1">
|
||||||
|
<a class="nav-link d-flex align-items-center rounded-pill" href="#sidebar-pages" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-pages">
|
||||||
|
<i class="bi bi-file-break-fill"></i>
|
||||||
|
<span>Pages</span>
|
||||||
|
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||||
|
</a>
|
||||||
|
<div class="collapse" id="sidebar-pages">
|
||||||
|
<ul class="nav nav-sm flex-column mt-1">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/pages/other-pricing.html" class="nav-link">
|
||||||
|
Pricing Plans
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/pages/terms.html" class="nav-link">
|
||||||
|
Terms of Service
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/pages/error.html" class="nav-link">
|
||||||
|
Error Page
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a href="/index.html" class="nav-link">
|
||||||
|
Landing Page
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<!-- Divider -->
|
<!-- Divider -->
|
||||||
<hr class="navbar-divider my-5 opacity-20">
|
<hr class="navbar-divider my-5 opacity-70">
|
||||||
<!-- Navigation -->
|
|
||||||
|
|
||||||
<!-- Push content down
|
|
||||||
<div class="mt-auto"></div> -->
|
|
||||||
<!-- User (md) -->
|
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
<li class="nav-item">
|
<li>
|
||||||
<a class="nav-link" href="{% url 'login' %}">
|
<span class="nav-link text-xs font-semibold text-uppercase text-muted ls-wide">
|
||||||
<i class="bi bi-person-square"></i> Account
|
Resources
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item my-1">
|
||||||
|
<a class="nav-link d-flex align-items-center rounded-pill" href="{% url 'logout' %}">
|
||||||
|
<i class="bi bi-door-closed-fill"></i>
|
||||||
|
<span>Logout</span>
|
||||||
|
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item my-1">
|
||||||
<a class="nav-link" href="{% url 'logout' %}">
|
<a class="nav-link d-flex align-items-center rounded-pill" href="https://themes.getbootstrap.com/product/satoshi-defi-and-crypto-exchange-theme">
|
||||||
<i class="bi bi-box-arrow-left"></i> Logout
|
<i class="bi bi-journal-album"></i>
|
||||||
|
<span>Changelog</span>
|
||||||
|
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto">v1.0</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div class="mt-auto"></div>
|
||||||
|
<div class="card bg-dark border-0 mt-5 mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="vstack gap-4">
|
||||||
|
<i class="bi bi-emoji-sunglasses-fill text-primary text-xl"></i>
|
||||||
|
<p class="text-sm text-white text-opacity-70">
|
||||||
|
Upgrade your account to Pro for even more examples.
|
||||||
|
</p>
|
||||||
|
<a href="/pages/pricing.html" class="btn btn-sm btn-primary w-full rounded-pill">
|
||||||
|
Upgade now<i class="bi bi-arrow-right ms-2"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div class="flex-lg-fill overflow-x-auto vstack h-lg-screen">
|
||||||
|
<div class="d-none d-lg-flex py-4">
|
||||||
|
<div class="flex-none">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class=" d-lg-none d-xxl-flex align-items-center gap-4 px-4 scrollable-x">
|
||||||
|
<div class="d-flex gap-2 text-xs">
|
||||||
|
<span class="text-heading font-semibold">Cryptos:</span>
|
||||||
|
<span class="text-muted">21,713</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2 text-xs">
|
||||||
|
<span class="text-heading font-semibold">Market Cap:</span>
|
||||||
|
<span class="text-muted">$871,322,862,585</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2 text-xs">
|
||||||
|
<span class="text-heading font-semibold">24h Vol:</span>
|
||||||
|
<span class="text-muted">$180,639,667,232</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock sidebar %}
|
{% endblock sidebar %}
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block header %}
|
{% block header %}
|
||||||
<!-- Banner -->
|
<!-- Banner -->
|
||||||
<a href="#" class="btn w-full btn-primary text-truncate rounded-0 py-2 border-0 position-relative" style="z-index: 1000;">
|
{% if messages %}
|
||||||
|
{% for message in messages %}
|
||||||
|
<a href="#"{% if message.tags %} class="btn {{ message.tags }} w-full text-truncate rounded-0 py-2 border-0 position-relative" style="z-index: 1000;" {% endif %}>{{ message }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
<!--
|
||||||
|
<a href="#" class="btn btn-danger w-full text-truncate rounded-0 py-2 border-0 position-relative" style="z-index: 1000;">
|
||||||
<strong>Strong</strong> message here→
|
<strong>Strong</strong> message here→
|
||||||
</a>
|
</a>
|
||||||
|
-->
|
||||||
|
|
||||||
{% endblock header %}
|
{% endblock header %}
|
|
@ -38,6 +38,7 @@ class User(AbstractUser):
|
||||||
username = None
|
username = None
|
||||||
email = models.EmailField(_('email address'), unique=True)
|
email = models.EmailField(_('email address'), unique=True)
|
||||||
percentage = models.DecimalField(decimal_places=3, default=0.010, max_digits=4)
|
percentage = models.DecimalField(decimal_places=3, default=0.010, max_digits=4)
|
||||||
|
vendornr = models.IntegerField(default=9)
|
||||||
|
|
||||||
objects = UserManager()
|
objects = UserManager()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue