Changeset cc4db18


Ignore:
Timestamp:
07/06/22 13:13:35 (2 years ago)
Author:
Danilo <danilo.najkov@…>
Branches:
master
Children:
899b19d
Parents:
d76b7ee
Message:

reservation module changes + contact module + menu module

Files:
14 added
15 edited

Legend:

Unmodified
Added
Removed
  • frontend/src/App.js

    rd76b7ee rcc4db18  
    1010import Reservations from "./Dashboard/Reservations";
    1111import FrontPage from "./FrontPage";
     12import Restaurant from "./Dashboard/Restaurant";
     13import Menu from "./Dashboard/Menu";
    1214
    1315
     
    3234          <Routes>
    3335            <Route path="/dashboard" element={loading ? <Spin /> : user ?<Dashboard setUser={setUser}/> :  <Navigate to="/login" replace={true} />}>
    34               <Route path="/dashboard" element={'edit restaurant'}/>
    35               <Route path="/dashboard/menu" element={'menu'}/>
     36              <Route path="/dashboard" element={<Restaurant/>}/>
     37              <Route path="/dashboard/menu" element={<Menu/>}/>
    3638              <Route path="/dashboard/reservations" element={<Reservations/>}/>
    3739              <Route path="/dashboard/reviews" element={'reviews'}/>
  • frontend/src/Dashboard.js

    rd76b7ee rcc4db18  
    2828                        to="/dashboard/reservations">Резервации</Link></Menu.Item>
    2929                    <Menu.Item key="4" icon={<QuestionCircleOutlined/>}><Link
    30                         to="/dashboard/reviews">Reviews</Link></Menu.Item>
    31                     <Menu.Item key="5" icon={<BookOutlined/>}><Link to="/dashboard/todo">Todo</Link></Menu.Item>
     30                        to="/dashboard/reviews">Оценки</Link></Menu.Item>
     31                    <Menu.Item key="5" icon={<BookOutlined/>}><Link to="/dashboard/todo">ToDo</Link></Menu.Item>
    3232                </Menu>
    3333            </div>
  • frontend/src/Dashboard/Reservations.js

    rd76b7ee rcc4db18  
    11import React, {useEffect, useState} from 'react'
    2 import {Button, Modal, DatePicker, List, notification} from "antd";
     2import {Button, Modal, DatePicker, List, notification, Input} from "antd";
    33import AddNewReservation from "./AddNewReservation";
    44import moment from 'moment'
     
    1313    const [date, setDate] = useState([moment().startOf('day'), moment().add(24,'hours')]);
    1414    const [reservations, setReservations] = useState([])
     15    const [newReservations, setNewReservations] = useState([])
    1516    const [loadingReservations, setLoadingReservations] = useState(true)
    1617
     
    2627                    to: date[1].format('YYYY-MM-DDThh:mm:ss') + 'Z'
    2728                }, headers: {Authorization: sessionStorage.getItem('Auth')}
    28             })
    29                 .then(res => {
     29            }).then(res => {
     30                axios.get(env.api + 'Reservations/new', {
     31                     headers: {Authorization: sessionStorage.getItem('Auth')}
     32                }).then(newres=>{
     33                    setNewReservations(newres.data);
    3034                    setReservations(res.data);
    31                     console.log(res.data)
    3235                    setLoadingReservations(false)
    33                 }).catch(er => {
     36                })
     37            }).catch(er => {
    3438                setLoadingReservations(false)
    3539                notification['error']({
    3640                    message: 'Се случи проблем при додавање резервација. Ве молиме пробајте повторно подоцна',
    3741                });
    38 
    3942                console.log(er);
    4043            });
     
    4851    const changeReservationStatus = (id, newStatus) => {
    4952        console.log(id)
    50         axios.put(env.api + 'Reservations/'+id,{}, {
     53        axios.put(env.api + 'Reservations/'+id+'/status',{}, {
    5154            params: {
    5255                status: newStatus
     
    5962            });
    6063            console.log(er);
    61         });
    62     }
     64        })
     65    }
     66    const changeTable = (ev, item) => {
     67        axios.put(env.api + 'Reservations/'+item.id+'/table',{}, {
     68            params: {
     69                tableId: ev.target.value == '' ? 0 : ev.target.value
     70            }, headers: {Authorization: sessionStorage.getItem('Auth')}
     71        }).then(res => {
     72            console.log("success");
     73        }).catch(er => {
     74            notification['error']({
     75                message: 'Се случи проблем при менување резервација. Ве молиме пробајте повторно подоцна',
     76            });
     77            console.log(er);
     78        })
     79        setNewReservations(old =>{
     80            const newRes = old.indexOf(item);
     81            if(newRes == -1) return old;
     82            const copy = [...old];
     83            copy[newRes].table = ev.target.value;
     84            return copy;
     85        })
     86        setReservations(old =>{
     87            const newRes = old.indexOf(item);
     88            if(newRes == -1) return old;
     89            const copy = [...old];
     90            copy[newRes].table = ev.target.value;
     91            return copy;
     92        })
     93    }
     94
    6395    const ReservationType = [ 'Кратко', 'Долго', 'Настан' ];
    6496    const ReservationPlace = [ 'Внатре', 'Надвор' ];
     
    84116                    border: '1px solid lightgray'
    85117                }} >
     118                    <h3 style={{float: 'left'}}>Нови резервации</h3>
     119                </div>
     120                <List loading={loadingReservations} dataSource={newReservations} itemLayout={'horizontal'} locale={{emptyText:'Немате нови резервации'}}
     121                      renderItem={item => (
     122                          <List.Item
     123                              actions={
     124                                  item.reservationStatus == 0 ?
     125                                      [<a onClick={()=>changeReservationStatus(item.id,ReservationStatus[1])}>Прифати</a>, <a style={{color:'red'}} onClick={()=>changeReservationStatus(item.id,ReservationStatus[2])}>Отфрли</a>]
     126                                      : item.reservationStatus == 1 ? [<span style={{color:'green'}}>Прифатено</span>] : [<span style={{color:'red'}}>Отфрлено</span>]}>
     127                              <div style={{display:'flex',flexDirection:'row',justifyContent:'space-between',width:'100%', padding:'10px'}}>
     128                                  <div style={{textAlign:'start', width:'200px'}}>
     129                                      <div><b>Име:</b> {item.contactName}</div>
     130                                      <div><b>Број:</b> {item.contactNumber}</div>
     131                                  </div>
     132                                  <div style={{marginTop:'10px'}}>
     133                                      Маса: <Input style={{ width: '60px' }} type={'number'} value={item.table == 0 ? '' : item.table} onChange={(ev)=>changeTable(ev,item)}/>
     134                                  </div>
     135                                  <div>
     136                                      <div><b>Луѓе:</b> {item.persons}</div>
     137                                      <div>{ReservationPlace[item.reservationPlace]}, {ReservationType[item.reservationType]}</div>
     138                                  </div>
     139                                  <div>
     140                                      <div style={{marginTop:'10px'}}>{moment(item.startDate).format('hh:mm DD/MM/YY')}</div>
     141                                  </div>
     142                              </div>
     143                          </List.Item>
     144                      )}/>
     145            </div>
     146            <div style={{marginTop:'20px', backgroundColor:'white'}}>
     147                <div style={{
     148                    width: '100%',
     149                    height:'75px',
     150                    backgroundColor: 'white',
     151                    padding: '20px',
     152                    border: '1px solid lightgray'
     153                }} >
    86154                    <h3 style={{float: 'left'}}>Сите резервации</h3>
    87155                    <RangePicker
     
    89157                        style={{float:'right'}}
    90158                        ranges={{
     159                            'Наредни 3 часа': [moment(), moment().add(3,'hours')],
    91160                            'Следни 24 часа': [moment().startOf('day'), moment().add(24,'hours')],
    92161                            'Овој месец': [moment().startOf('month'), moment().endOf('month')],
     
    96165                    />
    97166                </div>
    98                 <List loading={loadingReservations} dataSource={reservations} itemLayout={'horizontal'}
     167                <List loading={loadingReservations} dataSource={reservations} itemLayout={'horizontal'} locale={{emptyText:'Нема резервации за овој период'}}
    99168                      renderItem={item => (
    100169                          <List.Item
     
    107176                                    <div><b>Име:</b> {item.contactName}</div>
    108177                                    <div><b>Број:</b> {item.contactNumber}</div>
     178                                </div>
     179                                <div style={{marginTop:'10px'}}>
     180                                    Маса: <Input style={{ width: '60px' }} type={'number'} value={item.table == 0 ? '' : item.table} onChange={(ev)=>changeTable(ev,item)}/>
    109181                                </div>
    110182                                <div>
  • frontend/src/FrontPage.js

    rd76b7ee rcc4db18  
    1 import React, {useState} from 'react'
     1import React, {useState, useEffect} from 'react'
    22import {Header} from "./Header";
    33import {useNavigate} from "react-router-dom";
    4 import {Button, Image, Modal} from "antd";
     4import {Button, Card, Image, Modal, Spin} from "antd";
    55import placeholderImage from '../src/Assets/placeholder.png'
    66import AddNewReservation from "./Dashboard/AddNewReservation";
     7import axios from "axios";
     8import env from "./env";
    79
    810const FrontPage = ({}) => {
     
    1012    const [newReservationModal, setNewReservationModal] = useState(false);
    1113    const [saveModalLoading, setSaveModalLoading] = useState(false);
     14    const [loading, setLoading] = useState(true);
    1215    const history = useNavigate();
     16
     17    useEffect(()=>{
     18        setLoading(true);
     19        axios.get(env.api + 'Restaurants').then(res=>{
     20            setRestaurant(res.data);
     21            setLoading(false);
     22        });
     23    },[])
     24
    1325    return(
    1426        <div>
    1527            <Header onClickButton={()=>history('/login')} buttonText={'Логирај се'}/>
     28            {loading ? <Spin style={{margin: 20}}/> :
    1629                <div style={{
    1730                    height: '400px',
     
    2033                    padding: '20px'
    2134                }}>
    22                 <div id={'businessFrontImage'}>
    23                     <Image
    24                         src={restaurant.photo}
    25                         id={'businessFrontInsideImage'}
    26                         placeholder={
    27                             <img
    28                                 src={placeholderImage}
    29                                 alt={'place'}
    30                                 style={{width:'100%',height:'100%'}}
    31                             />
    32                         }
    33                         wrapperStyle={{overflow:'hidden',width:'100%'}}
    34                         style={{width:'100%', height:'100%',margin: 'auto',objectFit:'cover'}}>
    35                     </Image>
     35                    <div id={'businessFrontImage'}>
     36                        <Image
     37                            src={restaurant.base64Image}
     38                            id={'businessFrontInsideImage'}
     39                            placeholder={
     40                                <img
     41                                    src={placeholderImage}
     42                                    alt={'place'}
     43                                    style={{width: '100%', height: '100%'}}
     44                                />
     45                            }
     46                            wrapperStyle={{overflow: 'hidden', width: '100%'}}
     47                            style={{width: '100%', height: '100%', margin: 'auto', objectFit: 'cover'}}>
     48                        </Image>
     49                    </div>
     50                    <div id={'businessFrontInfo'}>
     51                        <div style={{
     52                            width: '100%',
     53                            height: '100%',
     54                            display: 'flex',
     55                            flexDirection: 'column',
     56                            justifyContent: 'space-between'
     57                        }}>
     58                            <div style={{flexGrow: 1, fontSize: '20px', fontWeight: '500', textAlign: 'start'}}>
     59                                {restaurant.name}
     60                                <div style={{
     61                                    color: 'gray',
     62                                    fontSize: '10px'
     63                                }}>{restaurant.address}, {restaurant.phone}</div>
     64                            </div>
     65                            <Button onClick={() => setNewReservationModal(true)}
     66                                    style={{width: '100%', marginTop: '5px'}} type={'primary'}>Резервирај</Button>
     67                        </div>
     68
     69                    </div>
     70                    <div style={{textAlign: 'start', backgroundColor: '#F2F2F2'}}>
     71                        <h2 style={{textAlign:'center',paddingTop:'20px'}}>Мени</h2>
     72                        {restaurant.menu.map(el =>
     73                            <Card title={el.title} style={{ width: 280, display:'inline-block', margin:'10px' }} size="small">
     74                                <p>{el.description}</p>
     75                                <b>{el.price} ден.</b>
     76                            </Card>
     77                        )}
     78                    </div>
    3679                </div>
    37                 <div id={'businessFrontInfo'} >
    38                     <div style={{width:'100%',height:'100%',display:'flex',flexDirection:'column',justifyContent:'space-between'}}>
    39                         <div style={{flexGrow:1, fontSize:'20px', fontWeight:'500', textAlign:'start'}}>
    40                             {restaurant.name}
    41                             <div style={{color:'gray',fontSize:'10px'}}>{restaurant.address}, {restaurant.city}</div>
    42                         </div>
    43                         <Button onClick={()=>setNewReservationModal(true)} style={{width:'100%',marginTop:'5px'}} type={'primary'}>Резервирај</Button>
    44                     </div>
    45 
    46                 </div>
    47             </div>
     80            }
    4881            <Modal
    4982                style={{top: 20}}
  • resTools_backend/backend/Controllers/ReservationsController.cs

    rd76b7ee rcc4db18  
    2727    }
    2828
     29    [Authorize]
     30    [HttpGet("new")]
     31    public async Task<List<ReservationResponse>> GetReservationsNew()
     32    {
     33        return await _reservationService.GetNewReservations();
     34    }
     35
    2936    [HttpPost()]
    3037    public async Task<IActionResult> CreateReservation([FromBody] CreateReservationRequest req)
     
    3542
    3643    [Authorize]
    37     [HttpPut("{rid}")]
     44    [HttpPut("{rid}/status")]
    3845    public async Task<IActionResult> ChangeStatus(int rid, [FromQuery] ReservationStatus status)
    3946    {
     
    4148        return Ok();
    4249    }
     50
     51    [Authorize]
     52    [HttpPut("{rid}/table")]
     53    public async Task<IActionResult> ChangeTable(int rid, [FromQuery] int tableId)
     54    {
     55        await _reservationService.AssignTable(tableId, rid);
     56        return Ok();
     57    }
    4358}
  • resTools_backend/backend/Controllers/RestaurantsController.cs

    rd76b7ee rcc4db18  
    3737        return response;
    3838    }
     39
     40    [HttpPost("upload")]
     41    public async Task<IActionResult> UploadImage([FromForm] IFormFile file)
     42    {
     43        await _restaurantService.UploadImage(file);
     44        return Ok();
     45    }
     46
     47    [Authorize]
     48    [HttpPut()]
     49    public async Task<IActionResult> UpdateRestaurant([FromBody] UpdateRestaurantRequest req)
     50    {
     51        await _restaurantService.UpdateRestaurant(req);
     52        return Ok();
     53    }
    3954}
  • resTools_backend/backend/DTOs/ReservationResponse.cs

    rd76b7ee rcc4db18  
    2222        [JsonProperty]
    2323        public string ContactNumber { get; set; }
     24        [JsonProperty]
     25        public int Table { get; set; }
    2426    }
    2527}
  • resTools_backend/backend/DTOs/RestaurantResponse.cs

    rd76b7ee rcc4db18  
    66    {
    77        [JsonProperty]
    8         public int Id { get; set; }
     8        public string Name { get; set; }
    99        [JsonProperty]
    10         public int? OwnerId { get; set; }
     10        public string Address { get; set; }
    1111        [JsonProperty]
    12         public string Name { get; set; }
     12        public string Phone { get; set; }
     13        [JsonProperty]
     14        public List<MenuItemResponse> Menu { get; set; }
     15        [JsonProperty]
     16        public string Base64Image { get; set; }
    1317    }
    1418}
  • resTools_backend/backend/Data/DataContext.cs

    rd76b7ee rcc4db18  
    5252        }
    5353
     54        private DbSet<MenuItem> menuItems;
     55        public DbSet<MenuItem> MenuItems
     56        {
     57            get
     58            {
     59                if (menuItems == null)
     60                {
     61                    menuItems = Set<MenuItem>();
     62                }
     63
     64                return menuItems;
     65            }
     66        }
     67
    5468
    5569        protected override void OnModelCreating(ModelBuilder modelBuilder)
     
    7488            .HasMany(p => p.Reservations)
    7589            .WithOne(b => b.Restaurant);
     90            modelBuilder.Entity<Restaurant>()
     91            .HasMany(p => p.Menu)
     92            .WithOne(b => b.Restaurant);
     93
    7694
    7795            //
     
    82100            .HasOne(p => p.Restaurant)
    83101            .WithMany(b => b.Reservations);
     102
     103            //
     104            // MenuItem
     105            //
     106            modelBuilder.Entity<MenuItem>().Property(x => x.Id).IsRequired().ValueGeneratedOnAdd();
     107            modelBuilder.Entity<MenuItem>()
     108            .HasOne(p => p.Restaurant)
     109            .WithMany(b => b.Menu);
    84110        }
    85111    }
  • resTools_backend/backend/Entities/Reservation.cs

    rd76b7ee rcc4db18  
    1414        public string ContactName { get; set; }
    1515        public string ContactNumber { get; set; }
     16        public int Table { get; set; }
    1617    }
    1718
  • resTools_backend/backend/Entities/Restaurant.cs

    rd76b7ee rcc4db18  
    99        public virtual User Owner { get; set; }
    1010        public virtual ICollection<Reservation> Reservations { get; set; }
     11        public virtual ICollection<MenuItem> Menu { get; set; }
    1112        public string Name { get; set; }
     13        public string Address { get; set; }
     14        public string Phone { get; set; }
     15        public byte[] Image { get; set; }
    1216    }
    1317}
  • resTools_backend/backend/Migrations/DataContextModelSnapshot.cs

    rd76b7ee rcc4db18  
    2323            NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
    2424
     25            modelBuilder.Entity("backend.Entities.MenuItem", b =>
     26                {
     27                    b.Property<int>("Id")
     28                        .ValueGeneratedOnAdd()
     29                        .HasColumnType("integer");
     30
     31                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
     32
     33                    b.Property<string>("Description")
     34                        .IsRequired()
     35                        .HasColumnType("text");
     36
     37                    b.Property<int>("Price")
     38                        .HasColumnType("integer");
     39
     40                    b.Property<int>("RestaurantId")
     41                        .HasColumnType("integer");
     42
     43                    b.Property<string>("Title")
     44                        .IsRequired()
     45                        .HasColumnType("text");
     46
     47                    b.HasKey("Id");
     48
     49                    b.HasIndex("RestaurantId");
     50
     51                    b.ToTable("MenuItems");
     52                });
     53
    2554            modelBuilder.Entity("backend.Entities.Reservation", b =>
    2655                {
     
    5786                        .HasColumnType("timestamp with time zone");
    5887
     88                    b.Property<int>("Table")
     89                        .HasColumnType("integer");
     90
    5991                    b.HasKey("Id");
    6092
     
    72104                    NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
    73105
     106                    b.Property<string>("Address")
     107                        .IsRequired()
     108                        .HasColumnType("text");
     109
     110                    b.Property<byte[]>("Image")
     111                        .IsRequired()
     112                        .HasColumnType("bytea");
     113
    74114                    b.Property<string>("Name")
    75115                        .IsRequired()
     
    78118                    b.Property<int?>("OwnerFk")
    79119                        .HasColumnType("integer");
     120
     121                    b.Property<string>("Phone")
     122                        .IsRequired()
     123                        .HasColumnType("text");
    80124
    81125                    b.HasKey("Id");
     
    106150
    107151                    b.ToTable("Users");
     152                });
     153
     154            modelBuilder.Entity("backend.Entities.MenuItem", b =>
     155                {
     156                    b.HasOne("backend.Entities.Restaurant", "Restaurant")
     157                        .WithMany("Menu")
     158                        .HasForeignKey("RestaurantId")
     159                        .OnDelete(DeleteBehavior.Cascade)
     160                        .IsRequired();
     161
     162                    b.Navigation("Restaurant");
    108163                });
    109164
     
    130185            modelBuilder.Entity("backend.Entities.Restaurant", b =>
    131186                {
     187                    b.Navigation("Menu");
     188
    132189                    b.Navigation("Reservations");
    133190                });
  • resTools_backend/backend/Program.cs

    rd76b7ee rcc4db18  
    4646builder.Services.AddScoped<IRestaurantService, RestaurantService>();
    4747builder.Services.AddScoped<IReservationService, ReservationService>();
     48builder.Services.AddScoped<IMenuService, MenuService>();
    4849builder.Services.AddScoped<ISmsService, SmsService>();
    4950
  • resTools_backend/backend/Services/ReservationService.cs

    rd76b7ee rcc4db18  
    1111        public Task ChangeReservationStatus(int resId, ReservationStatus status);
    1212        public Task<List<ReservationResponse>> GetReservatins(DateTime from, DateTime to);
     13        public Task<List<ReservationResponse>> GetNewReservations();
     14        public Task AssignTable(int tableId, int reservationId);
    1315    }
    1416    public class ReservationService : IReservationService
     
    2123            _context = context;
    2224            _smsService = smsService;
     25        }
     26
     27        public async Task AssignTable(int tableId, int reservationId)
     28        {
     29            var reservation = await _context.Reservations.FindAsync(reservationId);
     30            reservation.Table = tableId;
     31            _context.Update(reservation);
     32            await _context.SaveChangesAsync();
    2333        }
    2434
     
    5060        }
    5161
     62        public async Task<List<ReservationResponse>> GetNewReservations()
     63        {
     64            Restaurant res = await _context.Restoraunts
     65               .Include(x => x.Reservations
     66                   .Where(x => x.ReservationStatus == ReservationStatus.New))
     67               .FirstOrDefaultAsync();
     68            var reservations = res.Reservations.Select(t => new ReservationResponse()
     69            {
     70                ContactName = t.ContactName,
     71                ContactNumber = t.ContactNumber,
     72                Persons = t.Persons,
     73                StartDate = t.StartDate,
     74                ReservationStatus = t.ReservationStatus,
     75                Table = t.Table,
     76                Id = t.Id,
     77                ReservationPlace = t.ReservationPlace,
     78                ReservationType = t.ReservationType
     79            }).OrderByDescending(x => x.ReservationStatus == ReservationStatus.New).ToList();
     80            return reservations;
     81        }
     82
    5283        public async Task<List<ReservationResponse>> GetReservatins(DateTime from, DateTime to)
    5384        {
     
    6394                StartDate = t.StartDate,
    6495                ReservationStatus = t.ReservationStatus,
     96                Table = t.Table,
    6597                Id = t.Id,
    6698                ReservationPlace = t.ReservationPlace,
  • resTools_backend/backend/Services/RestaurantService.cs

    rd76b7ee rcc4db18  
    1010        public Task CreateRestaurant(string name, int userId);
    1111        public Task<RestaurantResponse> GetRestaurant();
     12        public Task UploadImage(IFormFile file);
     13        public Task UpdateRestaurant(UpdateRestaurantRequest req);
    1214    }
    1315    public class RestaurantService : IRestaurantService
     
    3234                .Select(x => new RestaurantResponse()
    3335                {
    34                     Id = x.Id,
    3536                    Name = x.Name,
    36                     OwnerId = x.OwnerFk,
     37                    Address = x.Address,
     38                    Phone = x.Phone,
     39                    Base64Image = String.Format("data:image/png;base64,{0}", Convert.ToBase64String(x.Image)),
     40                    Menu = x.Menu.Select(x => new MenuItemResponse()
     41                    {
     42                        Id = x.Id,
     43                        Title = x.Title,
     44                        Description = x.Description,
     45                        Price = x.Price
     46                    }).ToList()
    3747                })
    3848                .FirstOrDefaultAsync();
    3949            return res;
    4050        }
     51
     52        public async Task UpdateRestaurant(UpdateRestaurantRequest req)
     53        {
     54            var restaurant = await _context.Restoraunts.FirstOrDefaultAsync();
     55            restaurant.Name = req.Name;
     56            restaurant.Address = req.Address;
     57            restaurant.Phone = req.Phone;
     58            _context.Restoraunts.Update(restaurant);
     59            await _context.SaveChangesAsync();
     60        }
     61
     62        public async Task UploadImage(IFormFile file)
     63        {
     64            using (var memoryStream = new MemoryStream())
     65            {
     66                await file.CopyToAsync(memoryStream);
     67                var restaurant = await _context.Restoraunts.FirstOrDefaultAsync();
     68                restaurant.Image = memoryStream.ToArray();
     69                _context.Restoraunts.Update(restaurant);
     70                _context.SaveChanges();
     71            }
     72        }
    4173    }
    4274}
Note: See TracChangeset for help on using the changeset viewer.